This file will take you through recreating the figures presented in
the manuscript. Note that some of the figures went through additional
edits in Illustrator for additional labelling!
Preparation
Load packages
library(splancs)
Loading required package: sp
Warning: package ‘sp’ was built under R version 4.2.3
Spatial Point Pattern Analysis Code in S-Plus
Version 2 - Spatial and Space-Time analysis
Attaching package: ‘splancs’
The following object is masked from ‘package:tidyr’:
tribble
The following object is masked from ‘package:dplyr’:
tribble
load variables
parameters_tsukushi <- c(R1 = 8.89*10^6,
lambda = 3.7*10^5,
mu = 0.025,
p = 8*10^-6,
alpha = 1,
alphag = 2,
beta = 5.721,
mum = 48,
mug = 4,
I0 = 43.85965,
Ig0 = 0,
a = 150,
b = 100,
sp = 1,
psin = 16.69234,
psiw = 0.8431785,
phin = 0.03520591,
phiw = 550.842,
iota = 2.18*(10^6),
rho = 0.2627156)
# import in data files
ez_label <- read.csv(here("code_repository/data/ez_label.csv")) ## labelling schene
si_opt.df <- read.csv(here("code_repository/data/si_opt.csv")) ## optimized parameter + fitness list
si_dyn.df <- read_parquet(here("code_repository/data/si_dyn.parquet")) ## dynamics of single cue models
si_rn.df <- read_parquet(here("code_repository/data/si_rn.parquet")) ## reaction norms of single cue models
si_rug.df <- read_parquet(here("code_repository/data/si_rug.parquet")) ## data for rug plots
mc_all_fitness.df <- read_parquet(here("code_repository/data/mc_all_fitness.parquet")) ## fitness values for mc when all parameters are varying
mc_single_fitness.df <- read_parquet(here("code_repository/data/mc_single_fitness.parquet")) ## fitness values for mc when only one parameter is varying
dual_cue_f_lc.df <- read.csv(here("code_repository/data/dual_cue_fitness_local.csv")) ## optimized dual cue strategy using L-BFGS-B
dual_cue_f_glb.df <- read.csv(here("code_repository/data/dual_cue_fitness_global.csv")) ## optimized dual cue strategy using DEoptim + L-BFG
dual_cue_f_final.df <- read.csv(here("code_repository/data/dual_cue_fitness_final.csv")) ## final dataframe containing strategy that produced highest fitness (dual cue models)
dual_selected_cr.df <- read_parquet(here("code_repository/data/dual_selected_cr.parquet")) ## cr dynamics of selected models
cue_range_si_alt.df <- read.csv(here("code_repository/data/cue_range_si_alt.csv")) ## cue ranges for dual cue models
dual_cue_dyn.df <- read_parquet(here("code_repository/data/dual_cue_dyn.parquet")) ## dynamics of dual cue models
exp_ss.df <- read.csv(here("code_repository/data/experimental_data.csv")) ## cleaned experimental records of P. chabaudi infection
posterior.df <- read.csv(here("code_repository/data/posterior.csv")) ## posterior distribution of different parameter values
mc_burst.df <- read.csv(here("code_repository/data/mc_burst_opt.csv")) ## fitness values of cues when optimized with different beta values
mc_burst_single_input.df <- read.csv(here("code_repository/data/mc_burst_single_input.csv")) ## iput dataframe for simulating dyamics for for models optimized with different burst values (single cues only)
mc_burst_dual_input.df <- read.csv(here("code_repository/data/mc_burst_dual_input.csv")) ## ditto for R log + I log
mc_burst_final_diff.df <- read.csv(here("code_repository/data/mc_burst_final_diff.csv")) ## fitness of parasites adopting different parameters optimized for diff burst size but simulated with discordant burst size
mc_burst_rn.df <- read_parquet(here("code_repository/data/mc_burst_rn.parquet")) ## dataframe containing reaction norms of strains optimzied at various burst size filtered by relevant ranges (rug)
mc_burst_rn_RI.df <- read_parquet(here("code_repository/data/mc_burst_rn_RI.parquet")) ## dataframe containing reaction norms of strains optimzied at various burst size filtered by relevant ranges (rug)
mc_burst_rug.df_f <- read_parquet(here("code_repository/data/mc_burst_rug_f.parquet")) ## rug dataframe for parasites adopting the ideal strategy for various burst sizes
mc_burst_RI_rug.df <- read_parquet(here("code_repository/data/mc_burst_RI_rug.parquet")) ## dito but for R log and I log
mc_burst_dyn.df <- read_parquet(here("code_repository/data/mc_burst_dyn.parquet")) ## dynamics data for mc burst
si_dyn_30.df <- read_parquet(here("code_repository/data/si_dyn_30.parquet")) ## single cue dynamics data (30 days)
dual_cue_dyn_30.df <- read_parquet(here("code_repository/data/dual_cue_dyn_30.parquet")) ## dual cue dynamics (30 days)
## validation data (comparing fitness to random spline)
validation.ls <- list.files(here("code_repository/data/si_validation/"), full.names = T)
validation.df <- do.call(rbind, lapply(validation.ls, read.csv))
# import in code
source(here("code_repository/functions/chabaudi_si_clean.R"))
source(here("code_repository/functions/par_to_df.R"))
source(here("code_repository/functions/chabaudi_si_clean_high.R"))
source(here("code_repository/functions/par_to_hm.R"))
source(here("code_repository/functions/par_to_hm_te.R"))
# color codes
orange <- "#fc8d59"
blue <- "#4575b4"
#=================================# # Cue perception mediates fitness
#=================================# #———— A. conversion rate dynamics
and fitness values ————# ## function for dynamics generation
# function for getting single cue infection dynamics
get_si_dyn <- function(df){
## processing model input
par <- c(df$var1, df$var2, df$var3, df$var4) ## parameters
cue <- df$cue ## cue choice
log <- ifelse(df$log=="log", "log10", "none") ## log or not
cue_range <- seq(df$low, df$high, by = df$by) ## cue range
id <- df$id ## id
## get dynamics data
dyn <- chabaudi_si_clean(
parameters_cr = par,
parameters = parameters_tsukushi,
time_range = seq(0, 20, 0.001),
cue = cue,
cue_range = cue_range,
log_cue = log,
immunity = "tsukushi",
solver = "vode",
dyn = TRUE)
# append id
dyn2 <- cbind(dyn, id = rep(id, nrow(dyn)))
# return results
return(dyn2)
}
run function to generate data
get 30 day simulation data (not used for fitness but for plotting
purposes)
## get dynamics
si_opt.ls2 <- split(si_opt.df %>% filter(cue != "t"), seq(nrow(si_opt.df%>% filter(cue != "t"))))
si_dyn_30 <- mclapply(si_opt.ls2, get_si_dyn, mc.cores = 8)
## combine the dynamics file
si_dyn_30.df <- do.call(rbind, si_dyn_30)
si_dyn_30.df %>% group_by(id) %>% summarise(max_time = max(time))
## save
write_parquet(si_dyn_30.df, here("code_repository/data/si_dyn_30.parquet"))
process data to isolate conversion rate and pair them with
fitness
si_opt.df[si_opt.df$cue == "t",]$fitness_20
[1] 9.7879
plot
## plot together
fitness_rank_cr.plt <- ggarrange(fitness_rank.plt, fitness_cr.plt, widths = c(1.2, 1), align = "h", common.legend = T,
legend = "bottom")
Warning: Removed 11022 rows containing missing values or values outside the scale range (`geom_raster()`).
Warning: Removed 11022 rows containing missing values or values outside the scale range (`geom_raster()`).
#———— B. Reaction norms ————# ## function to obtain the cue values
sensed by parasites from dynamics datas
get_rug <- function(df){
## process cue
cue <- unique(df$cue)
## if cue contains "+", we need to first split them up and add them into the final dataframe
if(stringr::str_detect(cue, "\\+")){
cue_split <- stringr::str_split(string = cue, pattern = "\\+", simplify = T)
## get the two cues
cue_temp_1 <- cue_split[[1]]
cue_temp_2 <- cue_split[[2]]
## filter dyn
rug <- df %>%
filter(variable == cue_temp_1 | variable == cue_temp_2) %>%
dplyr::group_by(time) %>%
dplyr::mutate(sum = sum(value, na.rm = T)) %>%
select(time, value = sum, id)
}
# for cue with no addition, it is simply filtering for the values and returning it
if(stringr::str_detect(cue, "\\+", negate = T)){
rug <- df %>%
dplyr::filter(variable == cue) %>%
dplyr::select(time, value, id)}
return(rug)
}
run function to obtain cue values
# join dynamics data with cue information
si_dyn_cue.df <- left_join(si_dyn.df, si_opt.df, by = "id")
# split based on individual labels
si_dyn_cue.ls <- si_dyn_cue.df %>% group_split(id)
# run function to get rug
si_rug <- mclapply(si_dyn_cue.ls, get_rug, mc.cores = 6)
# combine and save
si_rug.df <- do.call(rbind, si_rug)
write_parquet(si_rug.df, here("code_repository/data/si_rug.parquet"))
function to obtain reaction norm
get_si_rn <- function(df){
## read in the parameter sets
par <- c(df$var1, df$var2, df$var3, df$var4)
## get cue range
cue_range <- seq(df$low, df$high, by = df$by)
## nested function to convert parameter set into basis spline function
rn <- par_to_df(par = par, cue_range = cue_range)
# if parasite is sensing logged cue, we have to exponentiate it back so they are on the same scale!
rn2 <- rn
if(stringr::str_detect(df$log, "log")){rn2$cue_range <- 10^(rn2$cue_range)}
# append label to reaction norm, dyn, and rug
rn2 <- data.frame(rn2, id = df$id)
return(rn2)
}
run function to get reaction norms
# split dataframe of optimized rcues
si_opt.ls <- split(si_opt.df, seq(nrow(si_opt.df)))
# run function
si_rn <- lapply(si_opt.ls, get_si_rn)
# bind together
si_rn.df <- do.call(rbind, si_rn)
write_parquet(si_rn.df, here("code_repository/data/si_rn.parquet"))
preparing the dataset for plotting reaction norms and rugs
## we are only plotting relevent ranges of the reaction norm, meaning that these are the cue values actually "sensed" by the parasites in an infection. We can use the rug dataframe to get a rough estimate of that
si_rug_lim.df <- si_rug.df %>%
group_by(id)%>%
summarise(min_temp = min(value, na.rm = T)*0.9,
max_temp = max(value, na.rm = T)*1.1) %>% ## calculate min and max for each cue
left_join(si_opt.df, by = "id") %>% ## join with si_opt so we can get the cmmon cue
ungroup() %>%
group_by(cue) %>%
summarise(min = min(min_temp), max = max(max_temp))
## process reaction norm data to restrict range and get label
si_rn.df_p <- si_rn.df %>%
left_join(select(si_opt.df, id, cue, log), by = "id") %>% ## get cue and log status from si_opt.df
left_join(si_rug_lim.df, by = "cue") %>% ## via cue, get ranges we want to limit the rn to
filter(cue_range <= max & cue_range >= min) %>% ## keep only cue values within range
left_join(select(ez_label, id, cue_label), by = "id") # get labels
## refactor to reorder the order at which the cues are presented
si_rn.df_p$cue_label <- factor(si_rn.df_p$cue_label,
levels = c("Asexual iRBC", "Sexual iRBC", "Total iRBC", "Gametocyte", "RBC"))
## split rug labels by none logged and logged
si_rug_cue.df <- si_rug.df %>%
distinct(id, value) %>% ## cut down on number of datapoints. For each id, keep only distinct points
left_join(select(si_opt.df, id, cue, log), by = "id") %>% ## get cue and log status from si_opt.df
left_join(select(ez_label, id, cue_label), by = "id") ## get cue label for plotting
## refactor rug
si_rug_cue.df$cue_label <- factor(si_rug_cue.df$cue_label,
levels = c("Asexual iRBC", "Sexual iRBC", "Total iRBC", "Gametocyte", "RBC"))
### split
si_rug_cue.df_none <- si_rug_cue.df %>% filter(log == "none")
si_rug_cue.df_log <- si_rug_cue.df %>% filter(log == "log")
plot reaction norms and associated rug plots
## note that geom_point contains a set of scripts aimed at thinning the data points so they do not overcrowd!
fitness_rn.plt <- ggplot() +
geom_line(data = si_rn.df_p, aes(x = cue_range, y = cr, color = log)) +
geom_point(data = si_rn.df_p %>%
mutate(rel_cue = round(cue_range/(max-min)*100)) %>%
distinct(id, rel_cue, .keep_all = T) %>%
filter(rel_cue %% 10 ==1),
aes(x = cue_range, y = cr, color = log, shape = log), size = 2) +
geom_rug(data = si_rug_cue.df_none, aes(x = value), color = "#fc8d59", sides = "t", length = unit(0.1, "npc")) +
geom_rug(data = si_rug_cue.df_log, aes(x = value), color = "#4575b4", sides = "b", length = unit(0.1, "npc")) +
facet_wrap(~cue_label, scales = "free_x", ncol = 1) +
labs(x = "Cue range", y = "Conversion rate", color = "Cue status", shape = "Cue status") +
ylim(0, 1) +
scale_color_manual(values=c( "#4575b4", "#fc8d59")) +
scale_x_continuous(labels = function(x) format(x, scientific = T),
guide = guide_axis(check.overlap = TRUE)) +
theme_bw() +
theme(axis.text.x = element_text(size = 8), legend.position = "bottom",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
#———— Plotting fitness, dynamics, and rn together ————#
ggarrange(fitness_rank_cr.plt, fitness_rn.plt, widths = c(2.7, 1), align = "h", ncol = 2, labels = c("A", "B"))
ggsave(units = "px", dpi = 300, width = 2250, height = 1500, filename = here("code_repository/figures/fitness_rn.tiff"), bg = "white", scale = 1.25)
#=================================# # Dual cue fitness main figure
#=================================# #————Relative ranking of dual cue vs
single cue fitness———–# ## Prepare dual cue dataset
## For the dual cue models, we optimized each cue combinations in 2 ways, one with L-BFGS-B with 0.5X9 starting point and another with DEoptim + L-FGBS-B. For each cue combination, we are going to pick the strategy that gave us the higher fitness
dual_cue_f_final.df <- dual_cue_f_glb.df %>%
select(id, id_b, label, label_b, fitness_glb = fitness,
par1_glb = par1, par2_glb = par2, par3_glb = par3, par4_glb = par4, par5_glb = par5, par6_glb = par6,
par7_glb = par7, par8_glb = par8, par9_glb = par9) %>%
left_join(
select(dual_cue_f_lc.df,
id, id_b, fitness_lc = fitness,
par1_lc = par1, par2_lc = par2, par3_lc = par3, par4_lc = par4, par5_lc = par5, par6_lc = par6,
par7_lc = par7, par8_lc = par8, par9_lc = par9), by = c("id", "id_b")
) %>% ## rename columns and join global and local optimization fitness dataframes
mutate(
fitness = ifelse(fitness_glb > fitness_lc, fitness_glb, fitness_lc),
par1 = ifelse(fitness_glb > fitness_lc, par1_glb, par1_lc),
par2 = ifelse(fitness_glb > fitness_lc, par2_glb, par2_lc),
par3 = ifelse(fitness_glb > fitness_lc, par3_glb, par3_lc),
par4 = ifelse(fitness_glb > fitness_lc, par4_glb, par4_lc),
par5 = ifelse(fitness_glb > fitness_lc, par5_glb, par5_lc),
par6 = ifelse(fitness_glb > fitness_lc, par6_glb, par6_lc),
par7 = ifelse(fitness_glb > fitness_lc, par7_glb, par7_lc),
par8 = ifelse(fitness_glb > fitness_lc, par8_glb, par8_lc),
par9 = ifelse(fitness_glb > fitness_lc, par9_glb, par9_lc)
) ## the columns are assigned based on which strategy produced the highest fitness
## write csv
write.csv(dual_cue_f_final.df, here("code_repository/data/dual_cue_fitness_final.csv"))
Combine single cue and dual cue dataset
dual_cue_f_final.df <- read.csv(here("code_repository/data/dual_cue_fitness_final.csv"))
dual_si_fitness.df <- dual_cue_f_final.df %>%
select(id, id_b, label, label_b, fitness_dual = fitness) %>%
left_join(select(si_opt.df, id, fitness_si = fitness_20), by = "id") %>% ## get fitness of single cue model based on first cue
left_join(select(si_opt.df, id_b = id, fitness_si_b = fitness_20), by = "id_b") %>% ## get fitness of single cue model based on second cue
mutate(fitness_si_final = ifelse(fitness_si > fitness_si_b, fitness_si, fitness_si_b)/9.883602,
label_comb = paste(label, "&", label_b), ## select the highest single cue model fitness
fitness_dual_norm = fitness_dual/9.883602)## normalize by time fitness
plot fitness rank

#————Time series conversion rate of dual cue models———–# Note that
our dual cue models take 9 parameters. To ensure that any difference
between dual and single cue models is due to the inclusion of additional
cues and NOT higher spline flexibility,we performed L-BFGS-B (local)
optimization of single cue models with df = 9 to control for the spline
flexibility.
dynamics simulation of high parameter cues
## best dual cue model: I log and R log
Rlog_Ilog.cr <- chabaudi_si_clean(
parameters_cr = c(4.446192033, 10.97518275, 1.38762817, 23.3059254, -3.452052371, -18.0070692, 39.66614226, -3.545193141, 18.78350799),
immunity = "tsukushi",
parameters = parameters_tsukushi,
time_range = seq(0, 20, by = 1e-3),
cue_range = seq(6, 7, by = 1/500),
cue_range_b = seq(0, log10(6*(10^6)), by = (log10(6*(10^6)))/500),
cue = "R",
cue_b = "I",
log_cue = "log10",
log_cue_b = "log10",
solver = "vode",
dyn = T
)
## when time is used as a cue (9 parameters). chabaudi_si_clean_high is simply a variation of chabaudi_si_clean that makes a more flexible spline!
time_high.cr <- chabaudi_si_clean_high(
parameters_cr = c(9.154314, -7.570829, -22.506638 , 3.382405 ,-13.453519 ,-17.011485 , 3.678181, -12.851895 ,-26.115158),
immunity = "tsukushi",
parameters = parameters_tsukushi,
time_range = seq(0, 20, by = 1e-3),
cue_range = seq(0, 20, by = 1e-3),
cue = "t",
solver = "vode",
dyn = T)
## when asexual iRBC is used as a cue (high flexibility)
I_high.cr <- chabaudi_si_clean_high(
parameters_cr = c(1.296675, 3.544034 , 4.907484, 2.174249, -3.238309 ,-5.181614 ,-1.645072 , 1.834302 , 1.581011),
immunity = "tsukushi",
parameters = parameters_tsukushi,
time_range = seq(0, 20, by = 1e-3),
cue_range = seq(0, log10(6*(10^6)), by = (log10(6*(10^6)))/5000),
cue = "I",
log_cue = "log10",
solver = "vode",
dyn = T)
## when RBC is used as cue (high flexibility)
R_high.cr <- chabaudi_si_clean_high(
parameters_cr = c(5.0340348 , 0.5846168 , 0.3749648 , 0.6842673 , 2.4748107 , 10.9036034 , 16.8246316, -24.8690971 ,1.8007238),
immunity = "tsukushi",
parameters = parameters_tsukushi,
time_range = seq(0, 20, by = 1e-3),
cue_range = seq(log10(10^6), log10(10^7), by = (log10(10^7)-log10(10^6))/5000),
cue = "R",
log_cue = "log10",
solver = "vode",
dyn = T)
plot
dual_selected_cr.pl <- ggplot() +
geom_line(data = dual_selected_cr.df, aes(color = label_new, x = time, y = value), size = 1) +
geom_point(data = dual_selected_cr.df %>% filter(time%%1 == 0), aes(color = label_new, x = time, y = value, shape = label_new), size = 3) +
labs(x = "Time (days)", y = "Conversion rate", color = "Cue(s)", shape = "Cue(s)") +
xlim(0, 20) +
scale_color_manual(values = c("#fc8d59","#fdcb44","black", "#4575b4")) +
theme_classic() +
theme(legend.position="right",
plot.margin = margin(t = 40, r = 0, b = 0, l = 0, unit = "pt")) +
guides(color = guide_legend(nrow = 4, byrow = TRUE))
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
#——– Reaction norm heatmap of R log10 + I log10 ————# # Process
data
# make heatmap data.frame
Rlog_Ilog.hm <- par_to_hm_te(par = c(4.446192033, 10.97518275, 1.38762817, 23.3059254, -3.452052371, -18.0070692, 39.66614226, -3.545193141, 18.78350799),
cue_range = seq(6, 7, length.out = 500),
cue_range_b = seq(0, 6.77815125, length.out = 500))
# process dynamics
Rlog_Ilog.dyn <- Rlog_Ilog.cr %>%
tidyr::pivot_wider(names_from = variable, values_from = value) %>%
mutate(log_R = log10(R),
log_I = log10(I))
plot
Rlog_Ilog_rn.pl <- ggplot() +
geom_raster(data = Rlog_Ilog.hm, aes(x = cue_range_b, y = cue_range, fill = cr)) +
scale_fill_viridis_c() +
geom_path(data = Rlog_Ilog.dyn, aes(x = log_I, y = log_R), color = "white", arrow = arrow(angle = 30, length = unit(0.1, "inches"))) +
geom_point(data = Rlog_Ilog.dyn %>% filter(row_number() %% 1000 == 1 & time <= 20), aes(x = log_I, y = log_R), color = "white") +
xlim(0.99*min(hablar::s(Rlog_Ilog.dyn$log_I), na.rm = T), 1.01* max(hablar::s(Rlog_Ilog.dyn$log_I), na.rm = T)) +
ylim(0.99*min(hablar::s(Rlog_Ilog.dyn$log_R), na.rm = T),1.01* max(hablar::s(Rlog_Ilog.dyn$log_R), na.rm = T)) +
labs(y = "RBC log10", x = "Asexual iRBC log10", fill = "Conversion\nrate") +
theme_classic() +
theme(legend.position = "right")
#———– Plot together ————#

#=================================# # Dual cue conversion rate
supplementary figure #=================================# ## Function to
simulate dual cue dynamics
dual_cue_dyn <- function(df){
## process cues
cue <- df$cue
cue_b <- df$cue_b
## process log
log <- ifelse(str_detect(df$id, "log"), "log10", "none")
log_b <- ifelse(str_detect(df$id_b, "log"), "log10", "none")
# process cue_range. ensure that both cue ranges are of the same length
cue_range <- seq(df$low, df$high, length.out = 500)
cue_range_b <- seq(df$low_b, df$high_b, length.out = 500)
# get parameter set
par <- c(df$par1, df$par2, df$par3, df$par4, df$par5, df$par6, df$par7, df$par8, df$par9)
# simulate dynamics
dyn <- chabaudi_si_clean(
parameters_cr = par,
immunity = "tsukushi",
parameters = parameters_tsukushi,
time_range = seq(0, 20, 0.01),
cue = cue,
cue_b = cue_b,
cue_range = cue_range,
cue_range_b = cue_range_b,
log_cue = log,
log_cue_b = log_b,
solver = "vode",
gam = "te",
dyn = T)
# return
dyn2 <- cbind(id = df$id, id_b = df$id_b,
label = df$label, label_b = df$label_b,
cue = cue, cue_b = cue_b, dyn)
write_parquet(dyn2, here(paste0("code_repository/data/dual_cue_dyn/", df$id, "_", df$id_b, ".parquet")))
}
Get dynamics of dual cue models
## For the dual cue fitness dataframe, add the cue range for each individual cue
dual_cue_f_final.df_p <- dual_cue_f_final.df %>%
left_join(select(cue_range_si_alt.df, id, cue, low, high), by = "id") %>%
left_join(select(cue_range_si_alt.df, id_b = id, cue_b = cue, low_b = low, high_b = high), by = "id_b")
## Split dataframes
dual_cue_f_final.ls <- split(dual_cue_f_final.df_p, seq(nrow(dual_cue_f_final.df_p)))
## Run function
mclapply(dual_cue_f_final.ls, dual_cue_dyn, mc.cores = 6)
## Concat all files
dual_cue_dyn.ls <- list.files(path = here("code_repository/data/dual_cue_dyn"), pattern = "*.parquet", full.names = T)
dual_cue_dyn.df <- do.call(rbind, lapply(dual_cue_dyn.ls, read_parquet))
write_parquet(dual_cue_dyn.df, here("code_repository/data/dual_cue_dyn.parquet"))
get 30 days dynamic (for plotting purposes)
## Run function
mclapply(dual_cue_f_final.ls, dual_cue_dyn, mc.cores = 6)
## Concat all files
dual_cue_dyn_30.ls <- list.files(path = here("code_repository/data/dual_cue_dyn_30"), pattern = "*.parquet", full.names = T)
dual_cue_dyn_30.df <- do.call(rbind, lapply(dual_cue_dyn_30.ls, read_parquet))
write_parquet(dual_cue_dyn_30.df, here("code_repository/data/dual_cue_dyn_30.parquet"))
Preparing dataset for plotting
## filter out only conversion rate and attach fitness values
dual_cue_cr.df <- dual_cue_dyn.df %>%
filter(variable == "cr") %>%
left_join(select(dual_cue_f_final.df, id, id_b, fitness), by = c("id", "id_b")) %>%
mutate(label_comb = paste(label, "&", label_b))
## Sanity check that the dynamics produced the same fitness as the optimized values. Yes!
dual_cue_dyn.df %>%
filter(variable == "tau_cum") %>%
filter(time == 20) %>%
distinct(id, id_b, value) %>%
left_join(select(dual_cue_f_final.df, id, id_b, fitness), by = c("id", "id_b")) %>%
mutate(diff = value-fitness)
Plot
ggsave(here("code_repository/figures/dual_cue_cr.tiff"), units = "px", width = 2000, height = 1500, dpi=300, bg = "white")
Warning: Removed 4080 rows containing missing values (`geom_raster()`).
#=================================# # Experimental disease maps of P.
chabaudi #=================================# ## Import in data
# import in https://academic.oup.com/emph/article/2018/1/127/5045871?login=true
## (2018 published in EMPH)
emph_2018 <- readxl::read_xls(here("code_repository/experimental_data/Huijben_2018_EMPH.xls"), sheet = 1)
# import in https://onlinelibrary.wiley.com/doi/10.1111/j.1558-5646.2010.01068.x
## (2010 published in Evolution)
evo_2010 <- readxl::read_xls(here("code_repository/experimental_data/Huijben_2010_evolution.xls"), sheet = 1)
# import in https://journals.plos.org/plospathogens/article?id=10.1371/journal.ppat.1003578#:~:text=The%20philosophy%20is%20that%20aggressive,longer%20feel%20sick%20%5B13%5D.
## (2013 in PLoS pathogen)
plos_2013_1 <- readxl::read_xlsx(here("code_repository/experimental_data/Huijben_2013_PLoS.xlsx"), sheet = 2)
plos_2013_2 <- readxl::read_xlsx(here("code_repository/experimental_data/Huijben_2013_PLoS.xlsx"), sheet = 3)
# import in https://onlinelibrary.wiley.com/doi/10.1111/j.1420-9101.2011.02369.x
## (2011 in Journal of Evolutionary Biology)
eseb_2011 <- readxl::read_xls(here("code_repository/experimental_data/Huijben_2011_eseb.xls"), sheet = 1)
# import in https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3939351/
## (2011 in Journal of American naturalist). Note private dataset so not provided in the supplementary!
amna_2011 <- readxl::read_xls(here("experimental_data/Pollitt_2011_naturalist.xls"), sheet = 1)
Clean data
## for EMPH 2018 study, include only infection series without drugs were R-inoculum is administered by itself, which includes 6, 7, 8, 9, 10. Box 6 has a starting inoculum number of 10^6, which is most similar to other studies. Filtering between day 3-21 because those are the days where we have single day data.
emph_2018_ss.df <- emph_2018 %>%
filter(Box %in% seq(6, 10) &
dplyr::between(Day, 3, 21)) %>%
mutate(dose = case_when(
Box == 6 ~ 10^6,
Box == 7 ~ 10^5,
Box == 8 ~ 10^3,
Box == 9 | Box == 10 ~ 10^1
)) %>%
mutate(strain = "As6p",
study = "emph2018",
study_strain = paste0(strain, study),
id = paste0(study, strain, Box, Mouse, 1),
RBC = RBC * (10^6)) %>%
select(day = Day,
mouse = Mouse,
RBC,
asex = Rasex,
gam = Rgam,
dose,
strain,
study,
study_strain,
id)
## for 2011 eseb, only day 3-17 data are analyzed because those are the days where gametocyte data are available
eseb_2011_ss.df <- eseb_2011 %>%
filter(Clones == "R" & between(Day, 3, 17) &
Drugs == "N") %>%
mutate(dose = 10^6,
strain = "As8p",
study = "eseb2011",
study_strain = paste0(strain, study),
RBC = RBC*(10^6),
id = paste0(study, strain, Box, Mouse, 2)) %>%
select(day = Day,
mouse = Mouse,
RBC,
asex = R.asex,
gam = R.gam,
dose,
strain,
study,
study_strain,
id)
## for evolution_2010, single infection data for both resistant and susceptible clones are available without drug treatment
evolution_2010_ss.df <- evo_2010 %>%
filter(Clone == "R" | Clone == "S") %>%
filter(between(Day, 3, 21) &
Drugs == "nodrugs") %>%
mutate(asex = R.asex + S.asex,
gam = R.gam + S.gam,
dose = 10^6,
RBC = RBC*(10^6),
study = "evol2011",
strain = ifelse(Clone == "R", "As12", "AJ51"),
study_strain = paste0(strain, "_", study),
id = paste0(study, strain, Box, Mouse, 3)) %>%
select(day = Day,
mouse = Mouse,
RBC,
asex,
gam,
dose,
strain,
study,
study_strain,
id)
## for amnat 2011, get single infection data. Filter out any mice that have missing data. Set negative asexuasl data ot 0
amna_2011_ss.df <- amna_2011 %>%
filter(treat %in% c("AJ", "AS", "ER", "CR", "CW", "DK")) %>%
mutate(asex = tot.para - tot.gcyte,
gam = tot.gcyte,
dose = 10^6,
study = "amna_2011",
RBC = rbc/(10^6),
study_strain = paste0(treat, "_", study),
id = paste0(study, treat, div, mouse, 4)) %>%
mutate(asex = ifelse(asex < 0, 0, asex)) # sometimes total parasite is less than gametocyte so need to correct for this
### check for NA by groups
amna_na.id <- amna_2011_ss.df %>%
filter_at(vars(asex, gam, RBC), all_vars(is.na(.))) %>%
distinct(id) %>%
select(id)
amna_2011_ss.df2 <- amna_2011_ss.df %>%
filter(!(id %in% amna_na.id$id)) %>%
select(day,
mouse,
RBC,
asex,
gam,
dose,
strain = treat,
study,
study_strain,
id)
## rbind
exp_ss.df <- rbind(emph_2018_ss.df, eseb_2011_ss.df, evolution_2010_ss.df, amna_2011_ss.df2)
## write
write.csv(exp_ss.df, here("code_repository/data/experimental_data.csv"))
Prepare dataset for plotting
names(exp_ss.df) <- c("X", "Day", "Mouse", "RBC", "iRBC", "Gametocyte", "Dose", "Strain", "Study", "Study_strain", "id")
## prepare a list of variable combinations we want to plot
exp_var.comb <- tidyr::expand_grid(x = c("RBC", "iRBC", "Gametocyte"),
y = c("RBC", "iRBC", "Gametocyte")) %>% ## get all pairwise combinations of variables
filter(x != y) %>% ## remove incidences where the 2 variables are the same
mutate(tmp = paste0(pmin(x, y), pmax(x, y))) %>% ## eliminate same variable but different order
slice_head(n = 1, by = tmp) %>%
select(-tmp)
List-wise plotting
## x and y axis are not logged!
exp_xy.pl_ls <- map2(exp_var.comb$x, exp_var.comb$y, ~ {
x_col <- .x
y_col <- .y
ggplot(exp_ss.df, aes_string(x = x_col, y = y_col)) +
geom_path(aes(colour = Day, group = id), arrow = arrow(type = "closed", angle = 10, length = unit(0, "inches"))) +
theme_classic() +
scale_color_viridis_c(option = "A", limits = c(3, 21)) +
labs(color = "Days post-infection") +
scale_y_continuous(labels = function(x) format(x, scientific = TRUE)) +
scale_x_continuous(labels = label_scientific(digits = 1))
}
)
## x-axis is logged
exp_xlogy.pl_ls <- map2(exp_var.comb$x, exp_var.comb$y, ~ {
x_col <- .x
y_col <- .y
ggplot(exp_ss.df, aes_string(x = sprintf("log10(%s)", x_col), y = y_col)) +
geom_path(aes(colour = Day, group = id), arrow = arrow(type = "closed", angle = 10, length = unit(0, "inches"))) +
theme_classic() +
scale_color_viridis_c(option = "A", limits = c(3, 21)) +
labs(color = "Days post-infection", x = paste(x_col, "log")) +
scale_y_continuous(labels = function(x) format(x, scientific = TRUE))
}
)
## y-axis logged
exp_xylog.pl_ls <- map2(exp_var.comb$x, exp_var.comb$y, ~ {
x_col <- .x
y_col <- .y
ggplot(exp_ss.df, aes_string(x = x_col, y = sprintf("log10(%s)", y_col))) +
geom_path(aes(colour = Day, group = id), arrow = arrow(type = "closed", angle = 10, length = unit(0, "inches"))) +
theme_classic() +
scale_color_viridis_c(option = "A", limits = c(3, 21)) +
labs(color = "Days post-infection", y = paste(y_col, "log")) +
scale_x_continuous(labels = label_scientific(digits = 1))
}
)
## both x and y axis is logged
exp_xlogylog.pl_ls <- map2(exp_var.comb$x, exp_var.comb$y, ~ {
x_col <- .x
y_col <- .y
ggplot(exp_ss.df, aes_string(x = sprintf("log10(%s)", x_col), y = sprintf("log10(%s)", y_col))) +
geom_path(aes(colour = Day, group = id), arrow = arrow(type = "closed", angle = 10, length = unit(0, "inches"))) +
theme_classic() +
scale_color_viridis_c(option = "A", limits = c(3, 21)) +
labs(color = "Days post-infection", x = paste(x_col, "log"), y = paste(y_col, "log"))
}
)
## plot together
ggarrange(plotlist = c(exp_xy.pl_ls, exp_xlogy.pl_ls, exp_xylog.pl_ls, exp_xlogylog.pl_ls),
common.legend = T, align = "hv")
ggsave(here("code_repository/figures/exp_disease-curve.tiff"), units = "px", width = 2250, height = 1500, scale = 1.4, dpi=300, bg = "white")
#=================================# # Simulated disease curve graph
#=================================# ## Function to obtain dynamics data
based on the dual cue input
get_dual_rn <- function(df){
## assign the two cues
cue <- unique(df$cue)
cue_b <- unique(df$cue_b)
## assign log status
log <- ifelse(str_detect(unique(df$id), "log"), "log", "none")
log_b <- ifelse(str_detect(unique(df$id_b), "log"), "log", "none")
## assign which cues are going to be logged
if(log == "log" & log_b == "none"){logged_cue <- cue}
if(log == "none" & log_b == "log"){logged_cue <- cue_b}
if(log == "log" & log_b == "log"){logged_cue <- c(cue, cue_b)}
if(log == "none" & log_b == "none"){logged_cue <- c()}
## keep variables that corresponds to the cue used
### for dataframes that does not involve combined variables such as I+Ig
if(isTRUE(str_detect(cue, "\\+", negate = T)) & isTRUE(str_detect(cue_b, "\\+", negate = T))){
df_f <- df %>%
filter(variable %in% c(cue, cue_b, "cr")) %>%
mutate(value = case_when(
variable %in% logged_cue ~ log10(value),
TRUE ~ value
)) %>% ## log transform values only when they match with the logged cue list
filter(value >= 0) ## filter out values <0, these happen due to stiffness of models but are not relevant
} else{
### assign both cues to a list
cue_ls <- c(cue, cue_b)
### pick the cue that has the "+" sign
combined_cue <- cue_ls[grepl("\\+", cue_ls)]
non_combined_cue <- cue_ls[!grepl("\\+", cue_ls)] ### this is the none combined cue
### unlist the cues
cue_unlist <- unlist(str_split(combined_cue, "\\+"))
### get filtered dataset containing only non-combine cue
df_f1 <- df %>%
filter(variable %in% c(non_combined_cue, "cr"))
### get filtered dataset containing combined cue. These will be summed up and bound back to the previous
df_f2 <- df %>%
filter(variable %in% cue_unlist) %>% ## keep only variables that we will combine
group_by(time) %>% ## for each time point, group the variables
mutate(value = sum(value, na.rm = T),
variable = combined_cue) %>% ## recalculate the value as sum of the values and reassign variable!
distinct(time, .keep_all = T) ## note that because we are mutating we must dedeuplicate the records
#### combine the two and log transform if necessary
df_f <- rbind(df_f1, df_f2) %>%
mutate(value = ifelse(variable %in% logged_cue, log10(value), value)) %>% ## log transform values only when they match with the logged cue list
filter(value >= 0)
}
## Convert dataframes wider such that the different variables have their own columns
df_fp <- df_f %>%
mutate(variable_id = ifelse(variable == cue, paste0(variable, "_", log), paste0(variable, "_", log_b))) %>% ### assign a unique variable id that could later be used to assign labels
left_join(select(ez_label, id, long_label), by = c("variable_id" = "id")) %>%
mutate(long_label = ifelse(variable == "cr", "cr", long_label)) %>% ## manually add cr
mutate(long_label = gsub(" ", "_", long_label)) %>% ## convert spaces to _ for plotting
pivot_wider(names_from = long_label, values_from = value, id_cols = c(time, id, id_b)) %>%
filter(time >= 1) %>% ## filter out day 0->1 because all cr = 0 before that
arrange(time) ## this is needed to prevent geom_path from joining the first and last data point
## assign NAs (meaning no stuff is produced yet to 0)
df_fp[is.na(df_fp)] <- 0
return(df_fp)
}
Run function to get a curated dataset containing only relevant
## split dual dynamics dataframe into list grouped by the dual cues
dual_cue_dyn.ls <- dual_cue_dyn.df %>% group_split(id, id_b)
## run function across list
dual_cue_rn.ls <- mclapply(dual_cue_dyn.ls, get_dual_rn, mc.cores = 6)
## sanity checks that we are actually summing iRBCs. Rings out
max(dual_cue_rn.ls[[2]]$Total_iRBC)
max(dual_cue_rn.ls[[4]]$Asexual_iRBC)
max(dual_cue_rn.ls[[6]]$Sexual_iRBC)
max(dual_cue_rn.ls[[10]]$Total_iRBC)
max(dual_cue_rn.ls[[12]]$Asexual_iRBC)
max(dual_cue_rn.ls[[14]]$Sexual_iRBC)
plot
## list apply all dataframes
cue_cue_rn_pl.ls <- lapply(dual_cue_rn.ls,
function(x){
## get names of columns used in the x and y axis
axis_cols <- setdiff(names(x), c("time", "id", "id_b", "cr"))
## ggplot
ggplot() +
geom_path(data = x, aes_string(x = axis_cols[[1]], y = axis_cols[[2]], color = "cr"),
arrow = arrow(length = unit(c(rep(0, nrow(x) - 2), 0.25), "inches")),
size = 1.5) +
geom_point(data = x %>% filter(time %% 1 == 0),
aes_string(x = axis_cols[[1]], y = axis_cols[[2]]), size = 1.5, shape = 1) +
theme_classic() +
scale_color_viridis_c(limits = c(0, 1)) +
labs(color = "Conversion rate", x = gsub("_", " ", axis_cols[[1]]), y = gsub("_", " ", axis_cols[[2]])) +
scale_x_continuous(labels = label_scientific(digits = 2)) +
scale_y_continuous(labels = label_scientific(digits = 2))
})
## arrange together
cue_cue_rn.pl <- ggarrange(plotlist = cue_cue_rn_pl.ls, ncol = 5, nrow = 8, common.legend = T, align = "hv")
ggsave(here("code_repository/figures/sim_disease-curve.tiff"), units = "px", width = 2250, height = 2500, scale = 2.2, dpi=300, bg = "white")
#=================================# # Curating list of selected exo
and simulated # disease curves (main figure)
#=================================# # plot
# main figure
ggarrange(
cue_cue_rn_pl.ls[[31]] +
scale_x_continuous(labels = scales::number_format(accuracy = 0.1)) +
scale_y_continuous(labels = scales::number_format(accuracy = 1)) +
theme(legend.position = "none"),
exp_xlogylog.pl_ls[[1]] + theme(legend.position = "none"),
cue_cue_rn_pl.ls[[35]] +
scale_y_continuous(labels = scales::number_format(accuracy = 1)) +
theme(legend.position = "none",
axis.text.x=element_text(size=6.5)),
exp_xylog.pl_ls[[1]] +
scale_y_continuous(labels = scales::number_format(accuracy = 1)) +
theme(legend.position = "none",
axis.text.x=element_text(size=6.5)),
cue_cue_rn_pl.ls[[30]] +
scale_x_continuous(labels = scales::number_format(accuracy = 0.1)) +
theme(legend.position = "none"),
exp_xlogy.pl_ls[[1]] + theme(legend.position = "none"),
cue_cue_rn_pl.ls[[15]] +
scale_x_continuous(labels = scales::number_format(accuracy = 0.1)) +
theme(legend.position = "none"),
exp_xlogy.pl_ls[[2]] + theme(legend.position = "none"),
align = "hv", ncol = 4, nrow = 2
)
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.
ggsave(here("code_repository/figures/sim_exp_disease_curve_main.tiff"), units = "px", width = 2250, height = 1000, scale = 1.25, dpi=300, bg = "white")

# get legend separately
ggarrange(
cue_cue_rn_pl.ls[[31]] +
scale_x_continuous(labels = scales::number_format(accuracy = 0.1)) +
scale_y_continuous(labels = scales::number_format(accuracy = 1)) +
theme(legend.position = "top"),
exp_xlogylog.pl_ls[[1]] + theme(legend.position = "top"))
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
ggsave(here("code_repository/figures/sim_exp_disease_curve_legend.tiff"), units = "px", width = 2250, height = 500, scale = 1.25, dpi=300, bg = "white")

#=================================# # MC simulation of single cue and
dual cue infection #=================================# #———— Impact of
all parameter variation on fitness ————# ## Append all fitness data and
sanity checks
## Import all fitness data and make into single dataframe
mc_all.ls <- list.files(path = here("code_repository/data/mc_all_fitness"), pattern = "*.csv", full.names = T)
## filter out MC records containing R log & I log. These data have different headers and would need to be processed differently
mc_all_sc.ls <- mc_all.ls[!grepl("R_log10-I_log10*", mc_all.ls)]
mc_all_Rlog_Ilog.ls <- mc_all.ls[grepl("R_log10-I_log10*", mc_all.ls)]
## we are expecting 11*5010 = 55110 data files
length(mc_all_sc.ls)
length(mc_all_Rlog_Ilog.ls) ## 5010 records nice
## read and append
mc_all_fitness_rc.df <- do.call(rbind, mclapply(mc_all_sc.ls, function(x){df <- read.csv(x)}, mc.cores = 6))
mc_all_fitness_Rlog_Ilog.df <- do.call(rbind, mclapply(mc_all_Rlog_Ilog.ls, function(x){df <- read.csv(x)}, mc.cores = 6))
## just correct the log -> log10 for consistency purpose and also rename id to iter
mc_all_fitness.df <- mc_all_fitness_Rlog_Ilog.df %>%
mutate(cue = "R log & I log", log = "log10") %>% ## renaming the cue names of R log and I log data so they fit
select(-c(cue_b, log_b)) %>%
rbind(mc_all_fitness_rc.df) %>%
mutate(log = ifelse(log == "log10", "log", "none"),
iter = id) %>% select(-id)
## write
write_parquet(mc_all_fitness.df, here("code_repository/data/mc_all_fitness.parquet"))
## for each cue and log, id = 1 is where all parameters are default values. Given that we simulated the time step with 0.01 rather than 0.001, we need to check whether this higher time step alters the fitness values. We can see that the difference is very small ~0.002 so this will not affect any our outcomes.
mc_all_fitness.df %>% filter(iter == 1) %>%
left_join(select(si_opt.df, cue, log, fitness_20), by = c("cue", "log")) %>%
mutate(diff = max_fitness - fitness_20)
process data for plotting fitness
## attach label
mc_all_fitness.df_p <- mc_all_fitness.df %>%
left_join(ez_label, by = c("cue", "log")) %>% # get label
mutate(long_label = ifelse(cue == "R log & I log", "RBC log &\nasexual iRBC log", long_label),
long_label = ifelse(cue == "t", "Time", long_label)) ## assign label manually to dual cue data
## manually check that everything is assigned correctly
mc_all_fitness.df_p %>% distinct(long_label, cue, log)
## get the reference fitness (default parameter variation), which is where iter = 1
mc_all_fitness_ref.df <- mc_all_fitness.df_p %>% filter(iter == 1)
## get the rest of the data points (excluding iter == 1) and calculate median and mean
mc_all_fitness_rand.df <- mc_all_fitness.df_p %>%
filter(iter != 1)
## get mean and mode in a separate df
mc_all_fitness_sum.df <- mc_all_fitness_rand.df %>%
group_by(long_label) %>%
summarize(mean = mean(max_fitness),
median = median(max_fitness),
geom_mean = exp(mean(log(max_fitness))))
plot fitness variation

#———— Impact of individual parameter variation on fitness ————# ##
combine all single parameter variation files
## list of file paths linked to the single parameter files
mc_single.ls <- list.files(path = here("code_repository/data/mc_single_fitness"), pattern = "*.csv", full.names = T)
## filter out R log and I log data. these will be attached later
mc_single_sc.ls <- mc_single.ls[!grepl("R_log10-I_log10*", mc_single.ls)]
mc_single_Rlog_Ilog.ls <- mc_single.ls[grepl("R_log10-I_log10*", mc_single.ls)]
## check number of files
length(mc_single_sc.ls)
length(mc_single_Rlog_Ilog.ls)
## read and combine
mc_single_sc_fitness.df <- do.call(rbind, mclapply(mc_single_sc.ls, function(x) read.csv(x), mc.cores = 6))
mc_single_Rlog_Ilog_fitness.df <- do.call(rbind, mclapply(mc_single_Rlog_Ilog.ls, function(x) read.csv(x), mc.cores = 6))
## change id -> iter and correcting log label
mc_single_fitness.df <- mc_single_Rlog_Ilog_fitness.df %>%
mutate(cue = "R log & I log") %>% ## manually recode cue for the dual cue results
select(-c(cue_b, log_b)) %>% ## these columns are not present in the single cue models and are removed
rbind(mc_single_sc_fitness.df) %>%
mutate(log = ifelse(log == "log10", "log", "none"),
iter = id) %>% select(-id)
## write
write_parquet(mc_single_fitness.df, here("code_repository/data/mc_single_fitness.parquet"))
## sanity check. iter = 1 is where all parameters are default. Should produce the same fitness values!
mc_single_fitness.df %>% filter(iter == 1)
## sanity check. We should have the same values for iter 1 across all and single parameter datasets.
mc_single_fitness.df %>% filter(iter == 1)
process data for plotting
## note that for each iter across the "single" and "all" dataset, the parameter alteration is the same. Thus, for each data point at which all parameter are varied, there is a corresponding datapoint where only one parameter is varied. We can join these 2 dataset by iter and cue and log
mc_single_all_fitness.df <- mc_single_fitness.df %>%
left_join(select(mc_all_fitness.df, fitness_all = max_fitness, cue, log, iter), by = c("cue", "log", "iter"))
## make the dataframe into a long format such that all fitness variations are in a single column
mc_single_fitness.long <- mc_single_all_fitness.df %>%
filter(iter != 1) %>% ## filter out iter = 1, which does not have variation
left_join(select(mc_all_fitness_ref.df, fitness_ref = max_fitness, cue , log),
by = c("cue", "log")) %>% ## add in the deterministic fitness values
select(-c("X", "rho", "burst", "iota_N1", "iota_N2", "phi_N1", "phi_N2")) %>% # keep only fitness and associated labels
tidyr::pivot_longer(-c("cue", "log", "fitness_all", "iter", "fitness_ref")) %>% ## make long
mutate(parameter = gsub("fitness_", "", name)) ## isolate parameter being altered
## calculate degree deviation from deterministic values. note that rel_diff_single ranges from -1 to 1. -1 -> one variable perturbation is acting in the opposite direction to the overall perturbation caused by randomizing all variables. 0 parameter variation contributes very little, 1 -> one variable contributes a lot
mc_single_fitness.long_p <- mc_single_fitness.long %>%
mutate(diff_single = value-fitness_ref, ## pertubation to fitness caused by single parameter variation
diff_all = fitness_all-fitness_ref, ## pertubation to fintess caused by all parameter varying
rel_diff_single = diff_single/diff_all ## normalized pertunation to fitness (single parameter)
) %>%
left_join(ez_label, by = c("cue", "log")) %>% # get label
mutate(long_label = ifelse(cue == "R log & I log", "RBC log &\nasexual iRBC log", long_label)) ## maually assign label to dual cue data
## calculate summary statistics. this includes median, credible interval (contains 89% data points calculated via Highest Density Interval, which is better for skewed data)
mc_single_fitness.sum <- mc_single_fitness.long_p %>%
group_by(long_label, parameter) %>%
summarise(ci_lower = ci(rel_diff_single, method = "HDI", ci = 0.89)[[2]],
ci_higher = ci(rel_diff_single, method = "HDI", ci = 0.89)[[3]],
quantile_low = quantile(rel_diff_single, 0.025),
quantile_high = quantile(rel_diff_single, 0.975),
median = median(rel_diff_single),
mean = mean(rel_diff_single)) %>%
mutate(parameter_label = case_when( ## recode parameter values
parameter == "rho" ~ "RBC replenishment (ρ)",
parameter == "phin" ~ "Half-life indis (ϕn)",
parameter == "phiw" ~ "Half-life targeted (ϕw)",
parameter == "psin" ~ "Activation indis (ψn)",
parameter == "psiw" ~ "Activation targeted (ψw)",
parameter == "beta" ~ "Burst size (β)",
))
`summarise()` has grouped output by 'long_label'. You can override using the `.groups` argument.
## for violin plots, what we can do is plot out only the 89% credible interval so the graph is easier to interpret
mc_single_fitness.long_p_f <- mc_single_fitness.long_p %>%
left_join(select(mc_single_fitness.sum, long_label, parameter, ci_lower, ci_higher, parameter_label), by = c("long_label", "parameter")) %>%
filter(rel_diff_single >= ci_lower & rel_diff_single < ci_higher)
## arrange ordering of cues and parameters
mc_single_fitness.long_p_f$long_label <- factor(mc_single_fitness.long_p_f$long_label,
c("RBC log &\nasexual iRBC log",
"Asexual iRBC", "Asexual iRBC log",
"Sexual iRBC", "Sexual iRBC log",
"Total iRBC", "Total iRBC log",
"Gametocyte", "Gametocyte log",
"RBC", "RBC log"))
mc_single_fitness.long_p_f$parameter_label <- factor(mc_single_fitness.long_p_f$parameter_label,
c("Burst size (β)",
"RBC replenishment (ρ)",
"Half-life indis (ϕn)",
"Half-life targeted (ϕw)",
"Activation indis (ψn)",
"Activation targeted (ψw)"))
plotting 89% credible interval. By visual inspection of the
distribution, ci represented the distribution (more honestly) than
quantile, even when quantile seems to display larger differences of
logging cues.

#——— arrange plots together ———#
ggarrange(mc_all_fitness.pl, mc_partition.pl, align = "h", widths = c(1.3, 2), labels = c("A", "B"))
ggsave(units = "px", dpi = 300, width = 2250, height = 1500, filename = here("code_repository/figures/mc_fitness_partition.tiff"), bg = "white", scale = 1.2)
#==========================================# # Supplementary figure
on MC posterior parameter distribution
#==========================================# ## Process data
## manually make dataframe containing the parameter values used in the deterministic model
par_det.df <- data.frame(
parameter = c("rho", "phi_N1", "phi_N2", "iota_N1", "iota_N2", "burst"),
deterministic = c(2.627156e-01, 3.520591e-02, 5.508420e+02, 1.669234e+01, 8.431785e-01, 5.721000e+00 )
)
## make into long format
posterior.long <- posterior.df %>%
tidyr::pivot_longer(-id, names_to = "parameter") %>%
left_join(par_det.df, by = "parameter") %>% ## add in the deterministic parameter values
mutate(label = case_when(
parameter == "rho" ~ "RBC replenishment (ρ)",
parameter == "phi_N1" ~ "Half-life indis (ϕn)",
parameter == "phi_N2" ~ "Half-life targeted (ϕw)",
parameter == "iota_N1" ~ "Activation indis (ψn)",
parameter == "iota_N2" ~ "Activation targeted (ψw)",
parameter == "burst" ~ "Burst size (β)"
)) ## rename paramter values
plot
## portion of parameter values that does not need log-transforming
posterior_1.pl <- ggplot(posterior.long %>% filter(!parameter %in% c("phi_N1", "phi_N2", "iota_N1"))) +
geom_density(aes(x = value), fill = "grey") +
geom_vline(aes(xintercept = deterministic), linetype = "dashed") +
facet_wrap(~label, scales = "free") +
labs(x = "", y = "Density") +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
## Plots where the x-axis should be log-transformed so it is easier to read
posterior_2.pl <- ggplot(posterior.long %>% filter(parameter %in% c("phi_N1", "phi_N2", "iota_N1"))) +
geom_density(aes(x = value), fill = "grey") +
geom_vline(aes(xintercept = deterministic), linetype = "dashed") +
facet_wrap(~label, scales = "free") +
labs(x = "Value", y = "Density") +
scale_x_continuous(trans = "log10") +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
## plot together
ggarrange(posterior_1.pl, posterior_2.pl, ncol = 1, align = "hv")
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
## save
ggsave(units = "px", dpi = 300, width = 2000, height = 1300, filename = here("code_repository/figures/posterior.tiff"), bg = "white", scale = 1)

#==========================================# # Rank plot of parasite
fitness with only one parameter varying
#==========================================# ## Process data
## get median fitness values
mc_single_fitness.sum2 <- mc_single_fitness.long2 %>%
group_by(short_label, parameter_label) %>%
summarize(median = median(value),
mean = mean(value),
geo_mean = exp(mean(log(value))))
`summarise()` has grouped output by 'short_label'. You can override using the `.groups` argument.
plot

#==================================# # Deciphering why certain cues
are less susceptible to burst size variation
#==================================# #————— Optimized vs non-optimized
fitness against burst size variation————-# This could be attributed to
lower ability of cues (even with optimal reaction norm) to respond to
changed beta. Or it could be robustness! ## Process data for downstream
analysis (execute once)
## get list of files
mc_burst.ls <- list.files(here("code_repository/data/mc_burst_opt/"), pattern = "*.csv", full.names = T)
## read in and bind. Note that we are excluding the parameter values for now because R log + I log has 9 of them
mc_burst.df_raw <- do.call(rbind, lapply(mc_burst.ls, function(x){
df <- read.csv(x)
df_p <- df %>% select(id, cue, log, beta, fitness, default)
return(df_p)}))
## we note that the R log + I log has a slightly higher fitness than time (df = 3), which is due to the greater flexibility of spline. NOte in previous optimization, increasing the df = 9 allowed time based cue to achieve higher fitness than R log + I log
mc_burst.df_raw %>% filter(id == "R_log+I_log")
## we optimized each cue (and burst size) via 2 methods: one starting from the optimal parameter size when beta = 5.721 (default = F) and one starting at (0.5x4) (default = T). We are going to pick the whichever one of them gave the highest fitness
mc_burst.df <- mc_burst.df_raw %>%
filter(id != "time") %>% ## time is ignored here because irrelavance
group_by(id, beta) %>%
top_n(1, fitness) %>% ### pick the one with the highest fitness
mutate(log = ifelse(log == "log10", "log", "none")) ## change log classification for consistency
## write
write.csv(mc_burst.df, here("code_repository/data/mc_burst_opt.csv"))
Process data for plotting
## list of burst size used in the optimization simulations
burst_ls <- c(3.98,4.7,5.53, 6.08)
## here, we want to see if after optimization, the optimized fitness values correlated with unoptimized fitness values. That is, is the decline of fitness caused by burst size variation due to limitations of the cue to achieve higher fitness or robustness of the cue?
posterior_burst_iter <- posterior.df %>%
filter(round(burst, digits = 2) %in% burst_ls) %>% ## get iterations that has a burst value close to the one we tested
mutate(diff_3 = abs(burst - burst_ls[[1]]),
diff_4 = abs(burst - burst_ls[[2]]),
diff_5 = abs(burst - burst_ls[[3]]),
diff_6 = abs(burst - burst_ls[[4]]))
## get iterations that we should take fitness values from
burst_iter_3 <- posterior_burst_iter[posterior_burst_iter$diff_3 == min(posterior_burst_iter$diff_3), "id"]
burst_iter_4 <- posterior_burst_iter[posterior_burst_iter$diff_4 == min(posterior_burst_iter$diff_4), "id"]
burst_iter_5 <- posterior_burst_iter[posterior_burst_iter$diff_5 == min(posterior_burst_iter$diff_5), "id"]
burst_iter_6 <- posterior_burst_iter[posterior_burst_iter$diff_6 == min(posterior_burst_iter$diff_6), "id"]
burst_iter_ls <- c(burst_iter_3, burst_iter_4, burst_iter_5, burst_iter_6)
## get joining dataframe to associate iteration number of burst size
burst_iter.df <- posterior.df %>%
filter(id %in% c( burst_iter_ls)) %>%
mutate(burst_round = round(burst, digits = 2)) %>%
select(iter = id, beta = burst_round)
mc_burst_fitness_final.df <- mc_single_fitness.long %>%
filter(parameter == "beta" & iter %in% c(burst_iter_ls)) %>% ## keep only rows where the parameter value altered is beta and belongs to the appropriate iterations
left_join(burst_iter.df, by = "iter") %>% ## get beta value associated with iterations
select(cue, log, fitness_unopt = value, beta) %>% ## select relevant columns
left_join(select(mc_burst.df, cue, log, beta, fitness_opt = fitness), by = c("cue", "log", "beta")) %>% ## join to get fitness value after optiization based on cue, log status, and beta value
left_join(select(ez_label, cue, log, short_label, long_label), by = c("cue", "log")) %>%
mutate(short_label = ifelse(cue == "R log & I log", cue, short_label)) %>% ## manually generate label for R log and I log
pivot_longer(cols = c(fitness_unopt, fitness_opt)) %>% ## make long
#select(-id) %>%
mutate(classification = ifelse(name == "fitness_unopt", "Unoptimized", "Optimized"))
## get mean values
mc_burst_fitness_med.df <- mc_burst_fitness_final.df %>%
group_by(classification, short_label) %>%
mutate(mean = mean(value),
geom_mean = exp(mean(log(value)))) %>%
distinct(cue, log, .keep_all = T) %>%
ungroup()
plot
## note that all points are ranked by increasing geometric mean within each facets!
mc_burst_fitness.pl <- ggplot() +
geom_line(data = mc_burst_fitness_final.df, aes(x = tidytext::reorder_within(short_label, value, classification, fun = function(x) exp(mean(log(x)))),
y = value, group = beta), color = "dark grey") +
geom_point(data = mc_burst_fitness_final.df, aes(x = tidytext::reorder_within(short_label, value, classification, fun = function(x) exp(mean(log(x)))),
y = value, color = beta), size = 2.5, alpha = 0.6) +
geom_line(data = mc_burst_fitness_med.df, aes(x = tidytext::reorder_within(short_label, value, classification, fun = function(x) exp(mean(log(x)))),
y = geom_mean, group = classification), size = 1.5) +
geom_point(data = mc_burst_fitness_med.df, aes(x = tidytext::reorder_within(short_label, value, classification, fun = function(x) exp(mean(log(x)))),
y = geom_mean), size = 3, shape = 17, color = "black") +
facet_wrap(~classification, ncol = 1, scales = "free") +
tidytext::scale_x_reordered() +
scale_color_viridis_c() +
labs(x = "Cue(s)", y = "Fitness", color = "Burst size") +
theme_bw() +
theme(
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.text.x = element_text(angle = 45, hjust=1))
#————— The advantage of I log is not due to default optimization
position—————-# Here, we will use the 4 optimized parameter at various
time point and obtain their fitness across the 3 other burst values ##
function to get fitness and conversion rate
get_mc_fitness_diff <- function(df, dual = F){
## list of burst values to test
burst_ls <- c(3.98,4.7,5.53, 5.721, 6.08)
## get parameter set
### assign the baseline parameter set
parameters_tsukushi_default <- c(R1 = 8.89*10^6,
lambda = 3.7*10^5,
mu = 0.025,
p = 8*10^-6,
alpha = 1,
alphag = 2,
beta = df$beta, ## beta is inserted here (place holder)
mum = 48,
mug = 4,
I0 = 43.85965,
Ig0 = 0,
a = 150,
b = 100,
sp = 1,
psin = 16.69234,
psiw = 0.8431785,
phin = 0.03520591,
phiw = 550.842,
iota = 2.18*(10^6),
rho = 0.2627156)
### get list of paramaters with all 4 values of beta
parameter_tsukushi_ls <- lapply(burst_ls, function(x) replace(parameters_tsukushi_default, 7, x))
## read in input for model simulation
if(isFALSE(dual)){
par <- c(df$par1, df$par2, df$par3, df$par4) ## parameter set
cue <- df$cue
log <- df$log
cue_range <- seq(df$low, df$high, df$by)
} else{
par <- c(df$par1, df$par2, df$par3, df$par4, df$par5, df$par6, df$par7, df$par8, df$par9)
cue <- "R"
cue_b <- "I"
log <- "log10"
log_b <- "log10"
cue_range <- seq(6, 7, length.out = 500)
cue_range_b <- seq(0, 6.77815125, length.out = 500)
}
## get dynamics data
if(isFALSE(dual)){
fitness_ls <- lapply(parameter_tsukushi_ls, function(x){
dyn <- chabaudi_si_clean(parameters_cr = par,
parameters = x,
immunity = "tsukushi",
time_range = seq(0, 20, 0.01),
cue = cue,
log_cue = log,
cue_range = cue_range,
solver = "vode",
dyn = T)
### process dynamics dataframe to add beta, cue, and log
dyn_p <- dyn %>% mutate(id = df$id, beta_optimized = df$beta, beta_simulated = x[[7]])
write_parquet(dyn_p, here(paste0("code_repository/data/mc_burst_dyn/", df$id, "_", gsub("\\.", "-", df$beta),
"_", gsub("\\.", "-", x[[7]]), "_dyn.parquet")))
### get fitness
fitness <- max(dyn[dyn$variable == "tau_cum", 3], na.rm = T)
fitness_beta <- c(beta_simulated = x[[7]], fitness = fitness) ## attach the beta used in the simulation here
return(fitness_beta)
})
} else{ ## dual cue models
fitness_ls <- lapply(parameter_tsukushi_ls, function(x){
dyn <- chabaudi_si_clean(parameters_cr = par,
parameters = x,
immunity = "tsukushi",
time_range = seq(0, 20, 0.01),
cue = cue,
cue_b = cue_b,
log_cue = log,
log_cue_b = log_b,
cue_range = cue_range,
cue_range_b = cue_range_b,
gam = "te",
solver = "vode",
dyn = T)
### process dynamics dataframe to add beta, cue, and log
dyn_p <- dyn %>% mutate(id = df$id, beta_optimized = df$beta, beta_simulated = x[[7]])
write_parquet(dyn_p, here(paste0("code_repository/data/mc_burst_dyn/", df$id, "_", gsub("\\.", "-", df$beta),
"_", gsub("\\.", "-", x[[7]]), "_dyn.parquet")))
### get fitness
fitness <- max(dyn[dyn$variable == "tau_cum", 3], na.rm = T)
fitness_beta <- c(beta_simulated = x[[7]], fitness = fitness) ## attach the beta used in the simulation here
return(fitness_beta)
}
)
}
## add identifiers to results (fitness)
res_final <- do.call(rbind, fitness_ls) %>%
as.data.frame() %>%
mutate(id = df$id,
cue = cue,
log = log,
beta_optimized = df$beta)
return(res_final)
}
get input dataframe for function to run
## get list of files
mc_burst.ls <- list.files(here("code_repository/data/mc_burst_opt/"), pattern = "*.csv", full.names = T)
mc_RI_burst.ls <- list.files(here("code_repository/data/mc_burst_opt/"), pattern = "R_log_I_log*", full.names = T)
## read in and bind. Exclude R log + I log due to differences in # of parameters
mc_burst_par.df_raw <- do.call(rbind, lapply(mc_burst.ls[!grepl("R_log_I_log", mc_burst.ls)], function(x){
df <- read.csv(x)
df_p <- df %>% select(id, cue, log, beta, fitness, par1, par2, par3, par4, default)
return(df_p)}))
mc_burst_RI.df_raw <- do.call(rbind, lapply(mc_RI_burst.ls, function(x){
df <- read.csv(x)
df_p <- df %>% select(id, cue, log, beta, fitness, par1, par2, par3, par4, par5, par6, par7, par8, par9, default)
return(df_p)}))
## get the top strategy based on fitness
mc_burst_par.df_f <- mc_burst_par.df_raw %>%
group_by(id, beta) %>%
top_n(1, fitness) %>%
filter(id != "time") ## get rid of time
mc_burst_RI.df <- mc_burst_RI.df_raw %>%
group_by(id, beta) %>%
top_n(1, fitness)
## join with si_opt to get cue ranges
mc_burst_par.df_fp <- mc_burst_par.df_f %>%
left_join(select(si_opt.df, id, low, high, by, long_label), by = "id")
## write
write.csv(mc_burst_par.df_fp, here("code_repository/data/mc_burst_single_input.csv"))
write.csv(mc_burst_RI.df, here("code_repository/data/mc_burst_dual_input.csv"))
process data for plotting
## get geometric mean. note that we are including cases where optimized beta = simulated beta to account for how good the rn is under optimal circumstances
mc_burst_diff.sum <- mc_burst_final_diff.df %>%
group_by(short_label) %>%
summarise(geom_mean = exp(mean(log(fitness)))) %>%
arrange(geom_mean)
## reorder x axis based on geometric mean
mc_burst_final_diff.df$short_label <- factor(mc_burst_final_diff.df$short_label, mc_burst_diff.sum$short_label)
plot
mc_burst_diff.pl <- ggplot() +
geom_violin(data = mc_burst_final_diff.df,
aes(x = short_label, y = fitness), color = "transparent", fill = "light grey") +
geom_jitter(data = mc_burst_final_diff.df,
aes(x = short_label, y = fitness, color = beta_optimized), size = 2, alpha = 0.7, width = 0.2) +
scale_color_viridis_c() +
geom_crossbar(data = mc_burst_diff.sum, aes(
x = short_label, y = geom_mean,
xmin = short_label, xmax = short_label, ymin = geom_mean, ymax = geom_mean),
size=1,col="black", width = .5) +
labs(x = "Cue(s)", y = "Fitness", color = "Optimized\nburst size") +
theme_classic() +
theme(axis.text.x = element_text(angle = 45, hjust=1))
#————— Changes in reaction norm in response to burst size
variation————-# ## Function to obtain dynamics data (rug) for parasites
adopting the optimal strategy for each burst size (execute once)
get_mc_burst_rug <- function(df, dual = F){
## get paramater set
parameters_tsukushi_burst <- c(R1 = 8.89*10^6,
lambda = 3.7*10^5,
mu = 0.025,
p = 8*10^-6,
alpha = 1,
alphag = 2,
beta = df$beta, ## beta is inserted here
mum = 48,
mug = 4,
I0 = 43.85965,
Ig0 = 0,
a = 150,
b = 100,
sp = 1,
psin = 16.69234,
psiw = 0.8431785,
phin = 0.03520591,
phiw = 550.842,
iota = 2.18*(10^6),
rho = 0.2627156)
## read in input
if(isFALSE(dual)){
par <- c(df$par1, df$par2, df$par3, df$par4) ## parameter set
cue <- df$cue
log <- df$log
cue_range <- seq(df$low, df$high, df$by)
} else{
par <- c(df$par1, df$par2, df$par3, df$par4, df$par5, df$par6, df$par7, df$par8, df$par9)
cue <- "R"
cue_b <- "I"
log <- "log10"
log_b <- "log10"
cue_range <- seq(6, 7, length.out = 500)
cue_range_b <- seq(0, 6.77815125, length.out = 500)
}
## get dynamics data
if(isFALSE(dual)){
dyn <- chabaudi_si_clean(parameters_cr = par,
parameters = parameters_tsukushi_burst,
immunity = "tsukushi",
time_range = seq(0, 20, 0.01),
cue = cue,
log_cue = log,
cue_range = cue_range,
dyn = T)
} else{
dyn <- chabaudi_si_clean(parameters_cr = par,
parameters = parameters_tsukushi_burst,
immunity = "tsukushi",
time_range = seq(0, 20, 0.01),
cue = cue,
cue_b = cue_b,
log_cue = log,
log_cue_b = log_b,
cue_range = cue_range,
cue_range_b = cue_range_b,
gam = "te",
dyn = T)
}
## get rug data only. Note that for dual cue models we are filtering out R and I
### filter to get only the relevant columns
if(isFALSE(dual)){
if(cue != "I+Ig"){
dyn_f <- dyn %>% filter(variable == cue)
} else{
dyn_f <- dyn %>%
filter(variable %in% c("I", "Ig")) %>%
group_by(time) %>%
summarise(value = sum(value, na.rm = T)) %>%
mutate(variable = "I+Ig")
}
} else{
dyn_f <- dyn %>% filter(variable %in% c("R", "I"))
}
### log-transform if needed. This does not needed to be ammended for dual cue models because both value is logged in the R log & I log model. Please change this if working with incidences where only one of the variable is log-transformed
if(log == "log10"){
dyn_fp <- dyn_f %>%
mutate(value = log10(value),
value = ifelse(value == -Inf, 0, value)) ## for -Inf (which is what happens when you log transform 0), reset to 0
} else{
dyn_fp <- dyn_f
}
## add identifiers to results
dyn_final <- dyn_fp %>%
mutate(id = df$id,
cue = cue,
log = log,
beta = df$beta)
return(dyn_final)
}
Prepare rug and reaction norm (execute once)
mc_burst_rn_RI.df_p <- mc_burst_rn_RI.df %>%
left_join(mc_burst_bound_RI %>% filter(variable == "R"), by = c("id", "beta")) %>%
left_join(mc_burst_bound_RI %>% filter(variable == "I") %>% select(id, beta, min_b = min, max_b = max), by = c("id", "beta")) %>%
filter(cue_range >= min & cue_range <= max & cue_range_b >= min_b & cue_range_b <= max_b)
Adding missing grouping variables: `variable`
Prepare data for plotting
## get label and only look at I log10 and I log + R log10 for comparison
mc_burst_dyn.df_p <- mc_burst_dyn.df %>%
filter(id %in% c("I_log","R_log+I_log") & variable == "cr" & beta_simulated != 5.721) %>% ## only get the cues we want to plot
left_join(ez_label, by = "id") %>%
mutate(long_short_label = ifelse(id == "R_log+I_log", "RBC log & asexual iRBC log\n(R log & I log)",
paste0(long_label, " (", short_label, ")")))
Plot
ggplot() +
geom_line(data = mc_burst_dyn.df_p %>% filter(beta_optimized != beta_simulated),
aes(x = time, y = value, group = beta_optimized, color = beta_optimized)) + ## only plotting unoptimized
geom_line(data = mc_burst_dyn.df_p %>% filter(beta_optimized == beta_simulated),
aes(x = time, y = value, group = beta_optimized, color = beta_optimized), linetype = "longdash", size = 1) + ## optimized cr is indicated by a dashed line
facet_grid(rows = vars(beta_simulated), cols = vars(long_short_label)) +
labs(x = "Time (days)", y = "Conversion rate", color = "Optimized\nburst size") +
scale_color_viridis_c() +
theme_bw() +
theme(
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
Warning: Removed 4 rows containing missing values (`geom_line()`).
Warning: Removed 4 rows containing missing values (`geom_line()`).

#—— arrange together!————-#
## first arrange the two fitness graphs
mc_burst_fitness_com.pl <- ggarrange(mc_burst_fitness.pl, mc_burst_diff.pl, ncol = 1, align = "hv", labels = c("A", "B"), heights = c(0.6,0.4))
Warning: Graphs cannot be horizontally aligned unless the axis parameter is set. Placing graphs unaligned.
## put them together
ggarrange(mc_burst_fitness_com.pl, mc_burst_cr.pl, ncol = 2, align = "hv", labels = c("", "C"), widths = c(0.5,0.55))
Warning: Removed 4 rows containing missing values (`geom_line()`).
Warning: Removed 4 rows containing missing values (`geom_line()`).
Warning: Graphs cannot be vertically aligned unless the axis parameter is set. Placing graphs unaligned.
Warning: Graphs cannot be horizontally aligned unless the axis parameter is set. Placing graphs unaligned.
ggsave(units = "px", dpi = 300, width = 2650, height = 1500, filename = here("code_repository/figures/mc_burst_main.tiff"), bg = "white", scale = 1.26)

#=================================# # Supplementary fig: all rn at
different burst sizes #=================================# ## Process
data for plotting
## get plotting label
mc_burst_rn.df_p <- mc_burst_rn.df %>%
left_join(select(ez_label, id, short_label), by = "id") %>%
mutate(long_short_label = paste0(long_label, " (", short_label, ")"))
## get plotting order. We are ranking them based on the geometric mean of unoptimized fitness (for all combinations)
mc_burst_order <- mc_burst_final_diff.df %>%
mutate(long_label = ifelse(id == "R_log+I_log", "RBC log & asexual iRBC log", long_label)) %>%
mutate(long_short_label = paste0(long_label, " (", short_label, ")")) %>%
group_by(long_short_label) %>%
summarise(geom_fitness = exp(mean(log(fitness)))) %>%
ungroup() %>%
arrange(-geom_fitness)
mc_burst_rn.df_p$long_short_label <- factor(mc_burst_rn.df_p$long_short_label, mc_burst_order$long_short_label)
## Normalize cue range for plotting
mc_burst_rn.df_p_n <- mc_burst_rn.df_p %>%
group_by(id) %>%
mutate(range = max(cue_range) - min(cue_range),
mean = mean(cue_range),
cue_range_norm = (cue_range - mean)/range)
## make rug plot of RI wider for trajectory plotting
mc_burst_RI_rug.wide <- mc_burst_RI_rug.df %>%
select(time, variable,value, beta) %>%
pivot_wider(names_from = variable, values_from = value, id_cols = c(time, beta))
mc_burst_RI_rug.wide
plot
## single cue
mc_burst_rn_sc.pl <- ggplot(data = mc_burst_rn.df_p_n) +
geom_line(aes(x = cue_range_norm, y = cr, color = beta, group = beta)) +
facet_wrap(~long_short_label, scales = "free", ncol = 3) +
labs(x = "Cue range (normalized)", y = "Conversion rate", color = "Burst size") +
scale_color_viridis_c() +
theme_bw() +
theme(legend.position = "bottom",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()) +
scale_x_continuous(labels = label_number(accuracy = 0.1))
## dual cue
mc_burst_rn_dc.pl <- ggplot() +
geom_raster(data = mc_burst_rn_RI.df, aes(y = cue_range, x = cue_range_b, fill = cr)) +
geom_path(data = mc_burst_RI_rug.wide, aes(x = I, y = R), size = 0.75, color = "cyan", arrow = arrow(angle = 30, length = unit(0.1, "inches"))) +
geom_point(data = mc_burst_RI_rug.wide %>% filter(time %% 1 == 0), aes(x = I, y = R), size = 1, color = "cyan") +
scale_fill_viridis_c(option = "C") +
facet_wrap(~beta, ncol = 1) +
labs(x = "Asexual iRBC log10", y = "RBC log10", fill = "Conversion\nrate") +
theme_bw() +
theme(legend.position = "bottom",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
plot.subtitle=element_text(size=13)) +
scale_y_continuous(labels = label_number(accuracy = 0.1),
limits = c(6.8,7))
## plot together
ggarrange(mc_burst_rn_sc.pl, mc_burst_rn_dc.pl, widths = c(2.5,1.1), labels = c("A", "B"), ncol = 2, nrow = 1, align = "hv")
ggsave(units = "px", dpi = 300, width = 2550, height = 1800, filename = here("code_repository/figures/mc_burst_rn.tiff"), bg = "white", scale = 1)
sanity check. RI rn is different between the beta != 3.98
mc_burst_rn_RI.df %>%
filter(beta != 3.98) %>%
group_by(cue_range, cue_range_b) %>%
mutate(sd = sd(cr)) %>%
ggplot() + geom_raster(aes(x = cue_range_b, y = cue_range, fill = sd))
#=====================================# # Impact of cue perception on
virulence #====================================# #——— Divid dynamics
data into high and low performing —————# # Single cue
top_4_single_cue$short_label
[1] "Ig log" "I+Ig log" "G log" "I log"
plot correlation
# single cue
si_corr_area.plt <- ggplot() +
geom_point(data = si_opt_area.df, aes(x = area, y = fitness_norm, color = "Single cue", shape = "Single cue"), size = 3, alpha = 0.7) +
geom_line(data = si_lm.ci, aes(x = area, fit)) +
geom_ribbon(data = si_lm.ci, aes(x = area, ymin = lwr, ymax = upr), alpha = .15) +
annotate(geom = "text", label = "R^2=0.18", x = 1.6*(10^11), y = 0.76) +
labs(x = "Area", y = "Normalized fitness", shape = "Cue category", color = "Cue category") +
scale_color_manual(values = c(orange)) +
xlim(min(si_opt_area.df$area), max(dual_cue_f_final_area.df$area)) +
theme_classic() +
theme(legend.position = "none")
# dual cue
dual_corr_area.plt <- ggplot() +
geom_point(data = dual_cue_f_final_area.df, aes(x = area, y = fitness_norm, color = "Dual cue", shape = "Dual cue"), size = 3, alpha = 0.7) +
geom_line(data = dual_lm.ci, aes(x = area, fit)) +
geom_ribbon(data = dual_lm.ci, aes(x = area, ymin = lwr, ymax = upr), alpha = .15) +
annotate(geom = "text", label = "R^2=0.11", x = 1.6*(10^11), y = 0.76) +
labs(x = "Area", y = "Normalized fitness", shape = "Cue category", color = "Cue category") +
scale_color_manual(values = c(blue)) +
xlim(min(si_opt_area.df$area), max(dual_cue_f_final_area.df$area)) +
theme_classic() +
theme(legend.position = "none")
#———assemble figure————#
si_dc_area.plt <- ggarrange(si_dc.plt, si_corr_area.plt, ncol = 2, nrow = 1, align = "hv", common.legend = T, labels = c("A", "B"))
dual_dc_area.plt <- ggarrange(dual_dc.plt, dual_corr_area.plt, ncol = 2, nrow = 1, align = "hv", common.legend = T, labels = c("C", "D"))
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_line()`).
ggarrange(si_dc_area.plt, dual_dc_area.plt, nrow = 2, align = "v")
ggsave(units = "px", dpi = 300, width = 2550, height = 2000, filename = here("code_repository/figures/virulence.tiff"), bg = "white", scale = 1)

#=============================# # Validation of optimization
#=============================# #—— process input data ——# # Process
data
## get difference betweent fitness conferred by model produced by a random spline strategy "V1" vs optimized fitness
validation.df_p <- validation.df %>%
left_join(select(si_opt.df, id, fitness_20, short_label, long_label), by = c("id")) %>%
mutate(diff = fitness_20-V1,
long_short_label = paste0(long_label, " (", short_label, ")"))
## no random strategy performed better!
validation.df_p %>% filter(diff<=0)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpUaGlzIGZpbGUgd2lsbCB0YWtlIHlvdSB0aHJvdWdoIHJlY3JlYXRpbmcgdGhlIGZpZ3VyZXMgcHJlc2VudGVkIGluIHRoZSBtYW51c2NyaXB0LiBOb3RlIHRoYXQgc29tZSBvZiB0aGUgZmlndXJlcyB3ZW50IHRocm91Z2ggYWRkaXRpb25hbCBlZGl0cyBpbiBJbGx1c3RyYXRvciBmb3IgYWRkaXRpb25hbCBsYWJlbGxpbmchCgojIFByZXBhcmF0aW9uCiMjIExvYWQgcGFja2FnZXMKYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkoZGVTb2x2ZSkKbGlicmFyeShjcm9uZSkKbGlicmFyeShvcHRpbVBhcmFsbGVsKQpsaWJyYXJ5KGRvUGFyYWxsZWwpCmxpYnJhcnkoZG9STkcpCmxpYnJhcnkoYXJyb3cpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShwYXJhbGxlbCkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KGJheWVzdGVzdFIpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoc3BsaW5lczIpCmxpYnJhcnkoc3BsYW5jcykKYGBgCgojIyBsb2FkIHZhcmlhYmxlcwpgYGB7cn0KcGFyYW1ldGVyc190c3VrdXNoaSA8LSBjKFIxID0gOC44OSoxMF42LCAKICAgICAgICAgICAgICAgIGxhbWJkYSA9IDMuNyoxMF41LAogICAgICAgICAgICAgICAgbXUgPSAwLjAyNSwgCiAgICAgICAgICAgICAgICBwID0gOCoxMF4tNiwgCiAgICAgICAgICAgICAgICBhbHBoYSA9IDEsIAogICAgICAgICAgICAgICAgYWxwaGFnID0gMiwgCiAgICAgICAgICAgICAgICBiZXRhID0gNS43MjEsIAogICAgICAgICAgICAgICAgbXVtID0gNDgsIAogICAgICAgICAgICAgICAgbXVnID0gNCwgCiAgICAgICAgICAgICAgICBJMCA9IDQzLjg1OTY1LCAKICAgICAgICAgICAgICAgIElnMCA9IDAsIAogICAgICAgICAgICAgICAgYSA9IDE1MCwgCiAgICAgICAgICAgICAgICBiID0gMTAwLCAKICAgICAgICAgICAgICAgIHNwID0gMSwKICAgICAgICAgICAgICAgIHBzaW4gPSAxNi42OTIzNCwKICAgICAgICAgICAgICAgIHBzaXcgPSAwLjg0MzE3ODUsCiAgICAgICAgICAgICAgICBwaGluID0gMC4wMzUyMDU5MSwgCiAgICAgICAgICAgICAgICBwaGl3ID0gNTUwLjg0MiwKICAgICAgICAgICAgICAgIGlvdGEgPSAyLjE4KigxMF42KSwKICAgICAgICAgICAgICAgIHJobyA9IDAuMjYyNzE1NikKCiMgaW1wb3J0IGluIGRhdGEgZmlsZXMKZXpfbGFiZWwgPC0gcmVhZC5jc3YoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZXpfbGFiZWwuY3N2IikpICMjIGxhYmVsbGluZyBzY2hlbmUKc2lfb3B0LmRmIDwtIHJlYWQuY3N2KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL3NpX29wdC5jc3YiKSkgIyMgb3B0aW1pemVkIHBhcmFtZXRlciArIGZpdG5lc3MgbGlzdApzaV9keW4uZGYgPC0gcmVhZF9wYXJxdWV0KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL3NpX2R5bi5wYXJxdWV0IikpICMjIGR5bmFtaWNzIG9mIHNpbmdsZSBjdWUgbW9kZWxzCnNpX3JuLmRmIDwtICByZWFkX3BhcnF1ZXQoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvc2lfcm4ucGFycXVldCIpKSAjIyByZWFjdGlvbiBub3JtcyBvZiBzaW5nbGUgY3VlIG1vZGVscwpzaV9ydWcuZGYgPC0gcmVhZF9wYXJxdWV0KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL3NpX3J1Zy5wYXJxdWV0IikpICMjIGRhdGEgZm9yIHJ1ZyBwbG90cwptY19hbGxfZml0bmVzcy5kZiA8LSByZWFkX3BhcnF1ZXQoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvbWNfYWxsX2ZpdG5lc3MucGFycXVldCIpKSAjIyBmaXRuZXNzIHZhbHVlcyBmb3IgbWMgd2hlbiBhbGwgcGFyYW1ldGVycyBhcmUgdmFyeWluZwptY19zaW5nbGVfZml0bmVzcy5kZiA8LSByZWFkX3BhcnF1ZXQoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvbWNfc2luZ2xlX2ZpdG5lc3MucGFycXVldCIpKSAjIyBmaXRuZXNzIHZhbHVlcyBmb3IgbWMgd2hlbiBvbmx5IG9uZSBwYXJhbWV0ZXIgaXMgdmFyeWluZwpkdWFsX2N1ZV9mX2xjLmRmIDwtIHJlYWQuY3N2KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL2R1YWxfY3VlX2ZpdG5lc3NfbG9jYWwuY3N2IikpICMjIG9wdGltaXplZCBkdWFsIGN1ZSBzdHJhdGVneSB1c2luZyBMLUJGR1MtQgpkdWFsX2N1ZV9mX2dsYi5kZiA8LSByZWFkLmNzdihoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX2N1ZV9maXRuZXNzX2dsb2JhbC5jc3YiKSkgIyMgb3B0aW1pemVkIGR1YWwgY3VlIHN0cmF0ZWd5IHVzaW5nIERFb3B0aW0gKyBMLUJGRwpkdWFsX2N1ZV9mX2ZpbmFsLmRmIDwtIHJlYWQuY3N2KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL2R1YWxfY3VlX2ZpdG5lc3NfZmluYWwuY3N2IikpICMjIGZpbmFsIGRhdGFmcmFtZSBjb250YWluaW5nIHN0cmF0ZWd5IHRoYXQgcHJvZHVjZWQgaGlnaGVzdCBmaXRuZXNzIChkdWFsIGN1ZSBtb2RlbHMpCmR1YWxfc2VsZWN0ZWRfY3IuZGYgPC0gcmVhZF9wYXJxdWV0KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL2R1YWxfc2VsZWN0ZWRfY3IucGFycXVldCIpKSAjIyBjciBkeW5hbWljcyBvZiBzZWxlY3RlZCBtb2RlbHMKY3VlX3JhbmdlX3NpX2FsdC5kZiA8LSByZWFkLmNzdihoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9jdWVfcmFuZ2Vfc2lfYWx0LmNzdiIpKSAjIyBjdWUgcmFuZ2VzIGZvciBkdWFsIGN1ZSBtb2RlbHMKZHVhbF9jdWVfZHluLmRmIDwtIHJlYWRfcGFycXVldChoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX2N1ZV9keW4ucGFycXVldCIpKSAjIyBkeW5hbWljcyBvZiBkdWFsIGN1ZSBtb2RlbHMKZXhwX3NzLmRmIDwtIHJlYWQuY3N2KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL2V4cGVyaW1lbnRhbF9kYXRhLmNzdiIpKSAjIyBjbGVhbmVkIGV4cGVyaW1lbnRhbCByZWNvcmRzIG9mIFAuIGNoYWJhdWRpIGluZmVjdGlvbgpwb3N0ZXJpb3IuZGYgPC0gcmVhZC5jc3YoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvcG9zdGVyaW9yLmNzdiIpKSAjIyBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIG9mIGRpZmZlcmVudCBwYXJhbWV0ZXIgdmFsdWVzCm1jX2J1cnN0LmRmIDwtIHJlYWQuY3N2KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX2J1cnN0X29wdC5jc3YiKSkgIyMgZml0bmVzcyB2YWx1ZXMgb2YgY3VlcyB3aGVuIG9wdGltaXplZCB3aXRoIGRpZmZlcmVudCBiZXRhIHZhbHVlcwptY19idXJzdF9zaW5nbGVfaW5wdXQuZGYgPC0gcmVhZC5jc3YoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvbWNfYnVyc3Rfc2luZ2xlX2lucHV0LmNzdiIpKSAjIyBpcHV0IGRhdGFmcmFtZSBmb3Igc2ltdWxhdGluZyBkeWFtaWNzIGZvciBmb3IgbW9kZWxzIG9wdGltaXplZCB3aXRoIGRpZmZlcmVudCBidXJzdCB2YWx1ZXMgKHNpbmdsZSBjdWVzIG9ubHkpCm1jX2J1cnN0X2R1YWxfaW5wdXQuZGYgPC0gcmVhZC5jc3YoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvbWNfYnVyc3RfZHVhbF9pbnB1dC5jc3YiKSkgIyMgZGl0dG8gZm9yIFIgbG9nICsgSSBsb2cKbWNfYnVyc3RfZmluYWxfZGlmZi5kZiA8LSByZWFkLmNzdihoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19idXJzdF9maW5hbF9kaWZmLmNzdiIpKSAjIyBmaXRuZXNzIG9mIHBhcmFzaXRlcyBhZG9wdGluZyBkaWZmZXJlbnQgcGFyYW1ldGVycyBvcHRpbWl6ZWQgZm9yIGRpZmYgYnVyc3Qgc2l6ZSBidXQgc2ltdWxhdGVkIHdpdGggZGlzY29yZGFudCBidXJzdCBzaXplIAptY19idXJzdF9ybi5kZiA8LSByZWFkX3BhcnF1ZXQoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvbWNfYnVyc3Rfcm4ucGFycXVldCIpKSAjIyBkYXRhZnJhbWUgY29udGFpbmluZyByZWFjdGlvbiBub3JtcyBvZiBzdHJhaW5zIG9wdGltemllZCBhdCB2YXJpb3VzIGJ1cnN0IHNpemUgZmlsdGVyZWQgYnkgcmVsZXZhbnQgcmFuZ2VzIChydWcpCm1jX2J1cnN0X3JuX1JJLmRmIDwtIHJlYWRfcGFycXVldChoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19idXJzdF9ybl9SSS5wYXJxdWV0IikpICMjIGRhdGFmcmFtZSBjb250YWluaW5nIHJlYWN0aW9uIG5vcm1zIG9mIHN0cmFpbnMgb3B0aW16aWVkIGF0IHZhcmlvdXMgYnVyc3Qgc2l6ZSBmaWx0ZXJlZCBieSByZWxldmFudCByYW5nZXMgKHJ1ZykKbWNfYnVyc3RfcnVnLmRmX2YgPC0gcmVhZF9wYXJxdWV0KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX2J1cnN0X3J1Z19mLnBhcnF1ZXQiKSkgIyMgcnVnIGRhdGFmcmFtZSBmb3IgcGFyYXNpdGVzIGFkb3B0aW5nIHRoZSBpZGVhbCBzdHJhdGVneSBmb3IgdmFyaW91cyBidXJzdCBzaXplcwptY19idXJzdF9SSV9ydWcuZGYgPC0gcmVhZF9wYXJxdWV0KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX2J1cnN0X1JJX3J1Zy5wYXJxdWV0IikpICMjIGRpdG8gYnV0IGZvciBSIGxvZyBhbmQgSSBsb2cKbWNfYnVyc3RfZHluLmRmIDwtIHJlYWRfcGFycXVldChoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19idXJzdF9keW4ucGFycXVldCIpKSAjIyBkeW5hbWljcyBkYXRhIGZvciBtYyBidXJzdApzaV9keW5fMzAuZGYgPC0gcmVhZF9wYXJxdWV0KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL3NpX2R5bl8zMC5wYXJxdWV0IikpICMjIHNpbmdsZSBjdWUgZHluYW1pY3MgZGF0YSAoMzAgZGF5cykKZHVhbF9jdWVfZHluXzMwLmRmIDwtIHJlYWRfcGFycXVldChoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX2N1ZV9keW5fMzAucGFycXVldCIpKSAjIyBkdWFsIGN1ZSBkeW5hbWljcyAoMzAgZGF5cykKCiMjIHZhbGlkYXRpb24gZGF0YSAoY29tcGFyaW5nIGZpdG5lc3MgdG8gcmFuZG9tIHNwbGluZSkKdmFsaWRhdGlvbi5scyA8LSBsaXN0LmZpbGVzKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL3NpX3ZhbGlkYXRpb24vIiksIGZ1bGwubmFtZXMgPSBUKQp2YWxpZGF0aW9uLmRmIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseSh2YWxpZGF0aW9uLmxzLCByZWFkLmNzdikpCgojIGltcG9ydCBpbiBjb2RlCnNvdXJjZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZnVuY3Rpb25zL2NoYWJhdWRpX3NpX2NsZWFuLlIiKSkKc291cmNlKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9mdW5jdGlvbnMvcGFyX3RvX2RmLlIiKSkKc291cmNlKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9mdW5jdGlvbnMvY2hhYmF1ZGlfc2lfY2xlYW5faGlnaC5SIikpCnNvdXJjZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZnVuY3Rpb25zL3Bhcl90b19obS5SIikpCnNvdXJjZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZnVuY3Rpb25zL3Bhcl90b19obV90ZS5SIikpCgojIGNvbG9yIGNvZGVzCm9yYW5nZSA8LSAiI2ZjOGQ1OSIKYmx1ZSA8LSAiIzQ1NzViNCIKYGBgCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIEN1ZSBwZXJjZXB0aW9uIG1lZGlhdGVzIGZpdG5lc3MKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIy0tLS0tLS0tLS0tLSBBLiBjb252ZXJzaW9uIHJhdGUgZHluYW1pY3MgYW5kIGZpdG5lc3MgdmFsdWVzIC0tLS0tLS0tLS0tLSMKIyMgZnVuY3Rpb24gZm9yIGR5bmFtaWNzIGdlbmVyYXRpb24KYGBge3J9CiMgZnVuY3Rpb24gZm9yIGdldHRpbmcgc2luZ2xlIGN1ZSBpbmZlY3Rpb24gZHluYW1pY3MKZ2V0X3NpX2R5biA8LSBmdW5jdGlvbihkZil7CiAgIyMgcHJvY2Vzc2luZyBtb2RlbCBpbnB1dAogIHBhciA8LSBjKGRmJHZhcjEsIGRmJHZhcjIsIGRmJHZhcjMsIGRmJHZhcjQpICMjIHBhcmFtZXRlcnMKICBjdWUgPC0gZGYkY3VlICMjIGN1ZSBjaG9pY2UKICBsb2cgPC0gaWZlbHNlKGRmJGxvZz09ImxvZyIsICJsb2cxMCIsICJub25lIikgIyMgbG9nIG9yIG5vdAogIGN1ZV9yYW5nZSA8LSBzZXEoZGYkbG93LCBkZiRoaWdoLCBieSA9IGRmJGJ5KSAjIyBjdWUgcmFuZ2UKICBpZCA8LSBkZiRpZCAjIyBpZAogIAogICMjIGdldCBkeW5hbWljcyBkYXRhCiAgZHluIDwtIGNoYWJhdWRpX3NpX2NsZWFuKAogICAgICAgICAgICBwYXJhbWV0ZXJzX2NyID0gcGFyLCAKICAgICAgICAgICAgcGFyYW1ldGVycyA9IHBhcmFtZXRlcnNfdHN1a3VzaGksIAogICAgICAgICAgICB0aW1lX3JhbmdlID0gc2VxKDAsIDIwLCAwLjAwMSksIAogICAgICAgICAgICBjdWUgPSBjdWUsIAogICAgICAgICAgICBjdWVfcmFuZ2UgPSBjdWVfcmFuZ2UsIAogICAgICAgICAgICBsb2dfY3VlID0gbG9nLAogICAgICAgICAgICBpbW11bml0eSA9ICJ0c3VrdXNoaSIsCiAgICAgICAgICAgIHNvbHZlciA9ICJ2b2RlIiwKICAgICAgICAgICAgZHluID0gVFJVRSkKICAKICAjIGFwcGVuZCBpZAogIGR5bjIgPC0gY2JpbmQoZHluLCBpZCA9IHJlcChpZCwgbnJvdyhkeW4pKSkKICAKICAjIHJldHVybiByZXN1bHRzCiByZXR1cm4oZHluMikKfQpgYGAKCiMjIHJ1biBmdW5jdGlvbiB0byBnZW5lcmF0ZSBkYXRhCmBgYHtyfQojIyBzcGxpdCBzaV9vcHQgaW50byBsaXN0CnNpX29wdC5scyA8LSBzcGxpdChzaV9vcHQuZGYsIHNlcShucm93KHNpX29wdC5kZikpKQoKIyMgZ2V0IGR5bmFtaWNzCnNpX2R5biA8LSBtY2xhcHBseShzaV9vcHQubHMsIGdldF9zaV9keW4sIG1jLmNvcmVzID0gOCkKCiMjIGNvbWJpbmUgdGhlIGR5bmFtaWNzIGZpbGUKc2lfZHluLmRmIDwtIGRvLmNhbGwocmJpbmQsIHNpX2R5bikKCiMjIHNhdmUKIyB3cml0ZV9wYXJxdWV0KHNpX2R5bi5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvc2lfZHluLnBhcnF1ZXQiKSkKYGBgCgojIyBnZXQgMzAgZGF5IHNpbXVsYXRpb24gZGF0YSAobm90IHVzZWQgZm9yIGZpdG5lc3MgYnV0IGZvciBwbG90dGluZyBwdXJwb3NlcykKYGBge3J9CiMjIGdldCBkeW5hbWljcwpzaV9vcHQubHMyIDwtIHNwbGl0KHNpX29wdC5kZiAlPiUgZmlsdGVyKGN1ZSAhPSAidCIpLCBzZXEobnJvdyhzaV9vcHQuZGYlPiUgZmlsdGVyKGN1ZSAhPSAidCIpKSkpCnNpX2R5bl8zMCA8LSBtY2xhcHBseShzaV9vcHQubHMyLCBnZXRfc2lfZHluLCBtYy5jb3JlcyA9IDgpCgojIyBjb21iaW5lIHRoZSBkeW5hbWljcyBmaWxlCnNpX2R5bl8zMC5kZiA8LSBkby5jYWxsKHJiaW5kLCBzaV9keW5fMzApCnNpX2R5bl8zMC5kZiAlPiUgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UobWF4X3RpbWUgPSBtYXgodGltZSkpCgojIyBzYXZlCndyaXRlX3BhcnF1ZXQoc2lfZHluXzMwLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9zaV9keW5fMzAucGFycXVldCIpKQpgYGAKCiMjIHByb2Nlc3MgZGF0YSB0byBpc29sYXRlIGNvbnZlcnNpb24gcmF0ZSBhbmQgcGFpciB0aGVtIHdpdGggZml0bmVzcwpgYGB7cn0KIyMga2VlcCBvbmx5IGNvbnZlcnNpb24gcmF0ZSBmcm9tIGR5bmFtaWNzCnNpX2R5bl9jci5kZiA8LSBzaV9keW4uZGYgJT4lIGZpbHRlcih2YXJpYWJsZSA9PSAiY3IiKQoKIyMgbGVmdCBqb2luIHdpdGggZml0bmVzcyB2YWx1ZXMKc2lfZHluX2NyX2ZpdG5lc3MuZGYgPC0gc2lfZHluX2NyLmRmICU+JSAKICBsZWZ0X2pvaW4oc2lfb3B0LmRmLCBieSA9ICJpZCIpIAoKIyMgbm9ybWFsaXplIGZpdG5lc3MgYnkgZGl2aWRpbmcgYWxsIHZhbHVlcyBieSB0aGUgZml0bmVzcyB2YWx1ZXMgd2UgZ2V0IHdoZW4gY3VlID0gdGltZQpzaV9vcHQuZGYgPC0gc2lfb3B0LmRmICU+JSAKICBtdXRhdGUoZml0bmVzc19ub3JtID0gZml0bmVzc18yMCAvIHNpX29wdC5kZltzaV9vcHQuZGYkY3VlID09ICJ0IixdJGZpdG5lc3NfMjApCmBgYAoKIyMgcGxvdApgYGB7cn0KIyMgZml0bmVzcyB2YWx1ZXMgcmFua2VkIGZyb20gaGlnaCB0byBsb3cKZml0bmVzc19yYW5rLnBsdCA8LSBnZ3Bsb3Qoc2lfb3B0LmRmLCBhZXMoeCA9IGZpdG5lc3Nfbm9ybSwgeSA9IGZjdF9yZW9yZGVyKGxvbmdfbGFiZWwsIGZpdG5lc3NfMjApKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImJsYWNrIikgKwogIGxhYnMoeCA9ICJOb3JtYWxpemVkIGZpdG5lc3MiLCB5ID0gIkN1ZSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHBsb3QubWFyZ2luID0gbWFyZ2luKDUuNSwgMCwgNS41LCA1LjUsICJwdCIpKQoKIyMgY29udmVyc2lvbiByYXRlIGR5bmFtaWMgcmFua2VkIGluIHRoZSBzYW1lIG9yZGVyCmZpdG5lc3NfY3IucGx0IDwtIGdncGxvdChzaV9keW5fY3JfZml0bmVzcy5kZiwgYWVzKHggPSB0aW1lLCB5ID0gZmN0X3Jlb3JkZXIobG9uZ19sYWJlbCwgZml0bmVzc18yMCksIGZpbGwgPSB2YWx1ZSkpICsKICBnZW9tX3Jhc3RlcigpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsKICBsYWJzKGZpbGwgPSAiQ29udmVyc2lvblxucmF0ZSIsIHggPSAiVGltZSAoZGF5cykiKSArCiAgeGxpbSgxLCAyMCkgKyAjIG5vdGUgdGhhdCBjciBhdCBkYXkgMC0+MSBpcyBhbHdheXMgMCBkdWUgdG8gaG93IHRoZSBtb2RlbCBpcyBzZXQgdXAhCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwgICNyZW1vdmUgeSBheGlzIGxhYmVscwogICAgICAgIGF4aXMudGlja3MueT1lbGVtZW50X2JsYW5rKCkgICNyZW1vdmUgeSBheGlzIHRpY2tzCiAgICAgICAgKQoKIyMgcGxvdCB0b2dldGhlcgpmaXRuZXNzX3JhbmtfY3IucGx0IDwtIGdnYXJyYW5nZShmaXRuZXNzX3JhbmsucGx0LCBmaXRuZXNzX2NyLnBsdCwgd2lkdGhzID0gYygxLjIsIDEpLCBhbGlnbiA9ICJoIiwgY29tbW9uLmxlZ2VuZCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZCA9ICJib3R0b20iKQpgYGAKCiMtLS0tLS0tLS0tLS0gQi4gUmVhY3Rpb24gbm9ybXMgLS0tLS0tLS0tLS0tIwojIyBmdW5jdGlvbiB0byBvYnRhaW4gdGhlIGN1ZSB2YWx1ZXMgc2Vuc2VkIGJ5IHBhcmFzaXRlcyBmcm9tIGR5bmFtaWNzIGRhdGFzCmBgYHtyfQpnZXRfcnVnIDwtIGZ1bmN0aW9uKGRmKXsKICAjIyBwcm9jZXNzIGN1ZQogIGN1ZSA8LSB1bmlxdWUoZGYkY3VlKQogIAogICMjIGlmIGN1ZSBjb250YWlucyAiKyIsIHdlIG5lZWQgdG8gZmlyc3Qgc3BsaXQgdGhlbSB1cCBhbmQgYWRkIHRoZW0gaW50byB0aGUgZmluYWwgZGF0YWZyYW1lCiAgIGlmKHN0cmluZ3I6OnN0cl9kZXRlY3QoY3VlLCAiXFwrIikpewogICAgY3VlX3NwbGl0IDwtIHN0cmluZ3I6OnN0cl9zcGxpdChzdHJpbmcgPSBjdWUsIHBhdHRlcm4gPSAiXFwrIiwgc2ltcGxpZnkgPSBUKQogICAgIyMgZ2V0IHRoZSB0d28gY3VlcyAKICAgIGN1ZV90ZW1wXzEgPC0gY3VlX3NwbGl0W1sxXV0KICAgIGN1ZV90ZW1wXzIgPC0gY3VlX3NwbGl0W1syXV0KICAgICMjIGZpbHRlciBkeW4KICAgIHJ1ZyA8LSBkZiAlPiUgCiAgICAgIGZpbHRlcih2YXJpYWJsZSA9PSBjdWVfdGVtcF8xIHwgdmFyaWFibGUgPT0gY3VlX3RlbXBfMikgJT4lIAogICAgICBkcGx5cjo6Z3JvdXBfYnkodGltZSkgJT4lIAogICAgICBkcGx5cjo6bXV0YXRlKHN1bSA9IHN1bSh2YWx1ZSwgbmEucm0gPSBUKSkgJT4lIAogICAgICBzZWxlY3QodGltZSwgdmFsdWUgPSBzdW0sIGlkKQogIH0KICAKICAjIGZvciBjdWUgd2l0aCBubyBhZGRpdGlvbiwgaXQgaXMgc2ltcGx5IGZpbHRlcmluZyBmb3IgdGhlIHZhbHVlcyBhbmQgcmV0dXJuaW5nIGl0CiAgaWYoc3RyaW5ncjo6c3RyX2RldGVjdChjdWUsICJcXCsiLCBuZWdhdGUgPSBUKSl7CiAgICBydWcgPC0gZGYgJT4lIAogICAgICBkcGx5cjo6ZmlsdGVyKHZhcmlhYmxlID09IGN1ZSkgJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KHRpbWUsIHZhbHVlLCBpZCl9CiAgCiAgcmV0dXJuKHJ1ZykKfQpgYGAKCiMjIHJ1biBmdW5jdGlvbiB0byBvYnRhaW4gY3VlIHZhbHVlcwpgYGB7cn0KIyBqb2luIGR5bmFtaWNzIGRhdGEgd2l0aCBjdWUgaW5mb3JtYXRpb24Kc2lfZHluX2N1ZS5kZiA8LSBsZWZ0X2pvaW4oc2lfZHluLmRmLCBzaV9vcHQuZGYsIGJ5ID0gImlkIikKCiMgc3BsaXQgYmFzZWQgb24gaW5kaXZpZHVhbCBsYWJlbHMKc2lfZHluX2N1ZS5scyA8LSBzaV9keW5fY3VlLmRmICU+JSBncm91cF9zcGxpdChpZCkKCiMgcnVuIGZ1bmN0aW9uIHRvIGdldCBydWcKc2lfcnVnIDwtIG1jbGFwcGx5KHNpX2R5bl9jdWUubHMsIGdldF9ydWcsIG1jLmNvcmVzID0gNikKCiMgY29tYmluZSBhbmQgc2F2ZQpzaV9ydWcuZGYgPC0gZG8uY2FsbChyYmluZCwgc2lfcnVnKQp3cml0ZV9wYXJxdWV0KHNpX3J1Zy5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvc2lfcnVnLnBhcnF1ZXQiKSkKYGBgCgojIyBmdW5jdGlvbiB0byBvYnRhaW4gcmVhY3Rpb24gbm9ybQpgYGB7cn0KZ2V0X3NpX3JuIDwtIGZ1bmN0aW9uKGRmKXsKICAjIyByZWFkIGluIHRoZSBwYXJhbWV0ZXIgc2V0cwogIHBhciA8LSBjKGRmJHZhcjEsIGRmJHZhcjIsIGRmJHZhcjMsIGRmJHZhcjQpCiAgIyMgZ2V0IGN1ZSByYW5nZQogIGN1ZV9yYW5nZSA8LSBzZXEoZGYkbG93LCBkZiRoaWdoLCBieSA9IGRmJGJ5KQogICMjIG5lc3RlZCBmdW5jdGlvbiB0byBjb252ZXJ0IHBhcmFtZXRlciBzZXQgaW50byBiYXNpcyBzcGxpbmUgZnVuY3Rpb24KICBybiA8LSBwYXJfdG9fZGYocGFyID0gcGFyLCBjdWVfcmFuZ2UgPSBjdWVfcmFuZ2UpCiAgCiAgIyBpZiBwYXJhc2l0ZSBpcyBzZW5zaW5nIGxvZ2dlZCBjdWUsIHdlIGhhdmUgdG8gZXhwb25lbnRpYXRlIGl0IGJhY2sgc28gdGhleSBhcmUgb24gdGhlIHNhbWUgc2NhbGUhIAogIHJuMiA8LSBybgogIGlmKHN0cmluZ3I6OnN0cl9kZXRlY3QoZGYkbG9nLCAibG9nIikpe3JuMiRjdWVfcmFuZ2UgPC0gMTBeKHJuMiRjdWVfcmFuZ2UpfQoKICAjIGFwcGVuZCBsYWJlbCB0byByZWFjdGlvbiBub3JtLCBkeW4sIGFuZCBydWcKICBybjIgPC0gZGF0YS5mcmFtZShybjIsIGlkID0gZGYkaWQpCiAgCiAgcmV0dXJuKHJuMikKfQpgYGAKCiMjIHJ1biBmdW5jdGlvbiB0byBnZXQgcmVhY3Rpb24gbm9ybXMKYGBge3J9CiMgc3BsaXQgZGF0YWZyYW1lIG9mIG9wdGltaXplZCByY3VlcwpzaV9vcHQubHMgPC0gc3BsaXQoc2lfb3B0LmRmLCBzZXEobnJvdyhzaV9vcHQuZGYpKSkKCiMgcnVuIGZ1bmN0aW9uCnNpX3JuIDwtIGxhcHBseShzaV9vcHQubHMsIGdldF9zaV9ybikKCiMgYmluZCB0b2dldGhlcgpzaV9ybi5kZiA8LSBkby5jYWxsKHJiaW5kLCBzaV9ybikKd3JpdGVfcGFycXVldChzaV9ybi5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvc2lfcm4ucGFycXVldCIpKQpgYGAKCiMjIHByZXBhcmluZyB0aGUgZGF0YXNldCBmb3IgcGxvdHRpbmcgcmVhY3Rpb24gbm9ybXMgYW5kIHJ1Z3MKYGBge3J9CiMjIHdlIGFyZSBvbmx5IHBsb3R0aW5nIHJlbGV2ZW50IHJhbmdlcyBvZiB0aGUgcmVhY3Rpb24gbm9ybSwgbWVhbmluZyB0aGF0IHRoZXNlIGFyZSB0aGUgY3VlIHZhbHVlcyBhY3R1YWxseSAic2Vuc2VkIiBieSB0aGUgcGFyYXNpdGVzIGluIGFuIGluZmVjdGlvbi4gV2UgY2FuIHVzZSB0aGUgcnVnIGRhdGFmcmFtZSB0byBnZXQgYSByb3VnaCBlc3RpbWF0ZSBvZiB0aGF0CnNpX3J1Z19saW0uZGYgPC0gc2lfcnVnLmRmICU+JSAKICBncm91cF9ieShpZCklPiUgCiAgc3VtbWFyaXNlKG1pbl90ZW1wID0gbWluKHZhbHVlLCBuYS5ybSA9IFQpKjAuOSwKICAgICAgICAgbWF4X3RlbXAgPSBtYXgodmFsdWUsIG5hLnJtID0gVCkqMS4xKSAlPiUgIyMgY2FsY3VsYXRlIG1pbiBhbmQgbWF4IGZvciBlYWNoIGN1ZQogIGxlZnRfam9pbihzaV9vcHQuZGYsIGJ5ID0gImlkIikgJT4lICMjIGpvaW4gd2l0aCBzaV9vcHQgc28gd2UgY2FuIGdldCB0aGUgY21tb24gY3VlCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShjdWUpICU+JSAKICBzdW1tYXJpc2UobWluID0gbWluKG1pbl90ZW1wKSwgbWF4ID0gbWF4KG1heF90ZW1wKSkgCgojIyBwcm9jZXNzIHJlYWN0aW9uIG5vcm0gZGF0YSB0byByZXN0cmljdCByYW5nZSBhbmQgZ2V0IGxhYmVsCnNpX3JuLmRmX3AgPC0gc2lfcm4uZGYgJT4lIAogIGxlZnRfam9pbihzZWxlY3Qoc2lfb3B0LmRmLCBpZCwgY3VlLCBsb2cpLCBieSA9ICJpZCIpICU+JSAjIyBnZXQgY3VlIGFuZCBsb2cgc3RhdHVzIGZyb20gc2lfb3B0LmRmCiAgbGVmdF9qb2luKHNpX3J1Z19saW0uZGYsIGJ5ID0gImN1ZSIpICU+JSAjIyB2aWEgY3VlLCBnZXQgcmFuZ2VzIHdlIHdhbnQgdG8gbGltaXQgdGhlIHJuIHRvCiAgZmlsdGVyKGN1ZV9yYW5nZSA8PSBtYXggJiBjdWVfcmFuZ2UgPj0gbWluKSAlPiUgIyMga2VlcCBvbmx5IGN1ZSB2YWx1ZXMgd2l0aGluIHJhbmdlCiAgbGVmdF9qb2luKHNlbGVjdChlel9sYWJlbCwgaWQsIGN1ZV9sYWJlbCksIGJ5ID0gImlkIikgIyBnZXQgbGFiZWxzCgoKIyMgcmVmYWN0b3IgdG8gcmVvcmRlciB0aGUgb3JkZXIgYXQgd2hpY2ggdGhlIGN1ZXMgYXJlIHByZXNlbnRlZApzaV9ybi5kZl9wJGN1ZV9sYWJlbCA8LSBmYWN0b3Ioc2lfcm4uZGZfcCRjdWVfbGFiZWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJBc2V4dWFsIGlSQkMiLCAiU2V4dWFsIGlSQkMiLCAiVG90YWwgaVJCQyIsICJHYW1ldG9jeXRlIiwgIlJCQyIpKQoKIyMgc3BsaXQgcnVnIGxhYmVscyBieSBub25lIGxvZ2dlZCBhbmQgbG9nZ2VkCnNpX3J1Z19jdWUuZGYgPC0gc2lfcnVnLmRmICU+JSAKICBkaXN0aW5jdChpZCwgdmFsdWUpICU+JSAjIyBjdXQgZG93biBvbiBudW1iZXIgb2YgZGF0YXBvaW50cy4gRm9yIGVhY2ggaWQsIGtlZXAgb25seSBkaXN0aW5jdCBwb2ludHMKICBsZWZ0X2pvaW4oc2VsZWN0KHNpX29wdC5kZiwgaWQsIGN1ZSwgbG9nKSwgYnkgPSAiaWQiKSAlPiUgIyMgZ2V0IGN1ZSBhbmQgbG9nIHN0YXR1cyBmcm9tIHNpX29wdC5kZgogIGxlZnRfam9pbihzZWxlY3QoZXpfbGFiZWwsIGlkLCBjdWVfbGFiZWwpLCBieSA9ICJpZCIpICMjIGdldCBjdWUgbGFiZWwgZm9yIHBsb3R0aW5nCgojIyByZWZhY3RvciBydWcKc2lfcnVnX2N1ZS5kZiRjdWVfbGFiZWwgPC0gZmFjdG9yKHNpX3J1Z19jdWUuZGYkY3VlX2xhYmVsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiQXNleHVhbCBpUkJDIiwgIlNleHVhbCBpUkJDIiwgIlRvdGFsIGlSQkMiLCAiR2FtZXRvY3l0ZSIsICJSQkMiKSkKCiMjIyBzcGxpdApzaV9ydWdfY3VlLmRmX25vbmUgPC0gc2lfcnVnX2N1ZS5kZiAlPiUgZmlsdGVyKGxvZyA9PSAibm9uZSIpCnNpX3J1Z19jdWUuZGZfbG9nIDwtIHNpX3J1Z19jdWUuZGYgJT4lIGZpbHRlcihsb2cgPT0gImxvZyIpCmBgYAoKIyMgcGxvdCByZWFjdGlvbiBub3JtcyBhbmQgYXNzb2NpYXRlZCBydWcgcGxvdHMKYGBge3J9CiMjIG5vdGUgdGhhdCBnZW9tX3BvaW50IGNvbnRhaW5zIGEgc2V0IG9mIHNjcmlwdHMgYWltZWQgYXQgdGhpbm5pbmcgdGhlIGRhdGEgcG9pbnRzIHNvIHRoZXkgZG8gbm90IG92ZXJjcm93ZCEKZml0bmVzc19ybi5wbHQgPC0gZ2dwbG90KCkgKwogIGdlb21fbGluZShkYXRhID0gc2lfcm4uZGZfcCwgYWVzKHggPSBjdWVfcmFuZ2UsIHkgPSBjciwgY29sb3IgPSBsb2cpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc2lfcm4uZGZfcCAlPiUgCiAgbXV0YXRlKHJlbF9jdWUgPSByb3VuZChjdWVfcmFuZ2UvKG1heC1taW4pKjEwMCkpICU+JSAKICAgIGRpc3RpbmN0KGlkLCByZWxfY3VlLCAua2VlcF9hbGwgPSBUKSAlPiUgCiAgICBmaWx0ZXIocmVsX2N1ZSAlJSAxMCA9PTEpLAogICAgICAgICAgICAgYWVzKHggPSBjdWVfcmFuZ2UsIHkgPSBjciwgY29sb3IgPSBsb2csIHNoYXBlID0gbG9nKSwgc2l6ZSA9IDIpICsKICAgZ2VvbV9ydWcoZGF0YSA9IHNpX3J1Z19jdWUuZGZfbm9uZSwgYWVzKHggPSB2YWx1ZSksIGNvbG9yID0gIiNmYzhkNTkiLCBzaWRlcyA9ICJ0IiwgbGVuZ3RoID0gdW5pdCgwLjEsICJucGMiKSkgKwogIGdlb21fcnVnKGRhdGEgPSBzaV9ydWdfY3VlLmRmX2xvZywgYWVzKHggPSB2YWx1ZSksIGNvbG9yID0gIiM0NTc1YjQiLCBzaWRlcyA9ICJiIiwgbGVuZ3RoID0gdW5pdCgwLjEsICJucGMiKSkgKwogIGZhY2V0X3dyYXAofmN1ZV9sYWJlbCwgc2NhbGVzID0gImZyZWVfeCIsIG5jb2wgPSAxKSArCiAgbGFicyh4ID0gIkN1ZSByYW5nZSIsIHkgPSAiQ29udmVyc2lvbiByYXRlIiwgY29sb3IgPSAiQ3VlIHN0YXR1cyIsIHNoYXBlID0gIkN1ZSBzdGF0dXMiKSArCiAgeWxpbSgwLCAxKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCAiIzQ1NzViNCIsICIjZmM4ZDU5IikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gZnVuY3Rpb24oeCkgZm9ybWF0KHgsIHNjaWVudGlmaWMgPSBUKSwKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGNoZWNrLm92ZXJsYXAgPSBUUlVFKSkgKyAKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojLS0tLS0tLS0tLS0tIFBsb3R0aW5nIGZpdG5lc3MsIGR5bmFtaWNzLCBhbmQgcm4gdG9nZXRoZXIgLS0tLS0tLS0tLS0tIwpgYGB7cn0KZ2dhcnJhbmdlKGZpdG5lc3NfcmFua19jci5wbHQsIGZpdG5lc3Nfcm4ucGx0LCB3aWR0aHMgPSBjKDIuNywgMSksIGFsaWduID0gImgiLCBuY29sID0gMiwgbGFiZWxzID0gYygiQSIsICJCIikpCmdnc2F2ZSh1bml0cyA9ICJweCIsIGRwaSA9IDMwMCwgd2lkdGggPSAyMjUwLCBoZWlnaHQgPSAxNTAwLCBmaWxlbmFtZSA9IGhlcmUoImNvZGVfcmVwb3NpdG9yeS9maWd1cmVzL2ZpdG5lc3Nfcm4udGlmZiIpLCBiZyA9ICJ3aGl0ZSIsIHNjYWxlID0gMS4yNSkKYGBgCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIER1YWwgY3VlIGZpdG5lc3MgbWFpbiBmaWd1cmUKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIy0tLS0tLS0tLS0tLVJlbGF0aXZlIHJhbmtpbmcgb2YgZHVhbCBjdWUgdnMgc2luZ2xlIGN1ZSBmaXRuZXNzLS0tLS0tLS0tLS0jCiMjIFByZXBhcmUgZHVhbCBjdWUgZGF0YXNldApgYGB7cn0KIyMgRm9yIHRoZSBkdWFsIGN1ZSBtb2RlbHMsIHdlIG9wdGltaXplZCBlYWNoIGN1ZSBjb21iaW5hdGlvbnMgaW4gMiB3YXlzLCBvbmUgd2l0aCBMLUJGR1MtQiB3aXRoIDAuNVg5IHN0YXJ0aW5nIHBvaW50IGFuZCBhbm90aGVyIHdpdGggREVvcHRpbSArIEwtRkdCUy1CLiBGb3IgZWFjaCBjdWUgY29tYmluYXRpb24sIHdlIGFyZSBnb2luZyB0byBwaWNrIHRoZSBzdHJhdGVneSB0aGF0IGdhdmUgdXMgdGhlIGhpZ2hlciBmaXRuZXNzCmR1YWxfY3VlX2ZfZmluYWwuZGYgPC0gZHVhbF9jdWVfZl9nbGIuZGYgJT4lIAogIHNlbGVjdChpZCwgaWRfYiwgbGFiZWwsIGxhYmVsX2IsIGZpdG5lc3NfZ2xiID0gZml0bmVzcywgCiAgICAgICAgIHBhcjFfZ2xiID0gcGFyMSwgcGFyMl9nbGIgPSBwYXIyLCBwYXIzX2dsYiA9IHBhcjMsIHBhcjRfZ2xiID0gcGFyNCwgcGFyNV9nbGIgPSBwYXI1LCBwYXI2X2dsYiA9IHBhcjYsCiAgICAgICAgIHBhcjdfZ2xiID0gcGFyNywgcGFyOF9nbGIgPSBwYXI4LCBwYXI5X2dsYiA9IHBhcjkpICU+JSAKICBsZWZ0X2pvaW4oCiAgICBzZWxlY3QoZHVhbF9jdWVfZl9sYy5kZiwKICAgICAgICAgaWQsIGlkX2IsIGZpdG5lc3NfbGMgPSBmaXRuZXNzLCAKICAgICAgICAgcGFyMV9sYyA9IHBhcjEsIHBhcjJfbGMgPSBwYXIyLCBwYXIzX2xjID0gcGFyMywgcGFyNF9sYyA9IHBhcjQsIHBhcjVfbGMgPSBwYXI1LCBwYXI2X2xjID0gcGFyNiwKICAgICAgICAgcGFyN19sYyA9IHBhcjcsIHBhcjhfbGMgPSBwYXI4LCBwYXI5X2xjID0gcGFyOSksIGJ5ID0gYygiaWQiLCAiaWRfYiIpCiAgKSAlPiUgIyMgcmVuYW1lIGNvbHVtbnMgYW5kIGpvaW4gZ2xvYmFsIGFuZCBsb2NhbCBvcHRpbWl6YXRpb24gZml0bmVzcyBkYXRhZnJhbWVzCiAgbXV0YXRlKAogICAgZml0bmVzcyA9IGlmZWxzZShmaXRuZXNzX2dsYiA+IGZpdG5lc3NfbGMsIGZpdG5lc3NfZ2xiLCBmaXRuZXNzX2xjKSwKICAgIHBhcjEgPSBpZmVsc2UoZml0bmVzc19nbGIgPiBmaXRuZXNzX2xjLCBwYXIxX2dsYiwgcGFyMV9sYyksCiAgICBwYXIyID0gaWZlbHNlKGZpdG5lc3NfZ2xiID4gZml0bmVzc19sYywgcGFyMl9nbGIsIHBhcjJfbGMpLAogICAgcGFyMyA9IGlmZWxzZShmaXRuZXNzX2dsYiA+IGZpdG5lc3NfbGMsIHBhcjNfZ2xiLCBwYXIzX2xjKSwKICAgIHBhcjQgPSBpZmVsc2UoZml0bmVzc19nbGIgPiBmaXRuZXNzX2xjLCBwYXI0X2dsYiwgcGFyNF9sYyksCiAgICBwYXI1ID0gaWZlbHNlKGZpdG5lc3NfZ2xiID4gZml0bmVzc19sYywgcGFyNV9nbGIsIHBhcjVfbGMpLAogICAgcGFyNiA9IGlmZWxzZShmaXRuZXNzX2dsYiA+IGZpdG5lc3NfbGMsIHBhcjZfZ2xiLCBwYXI2X2xjKSwKICAgIHBhcjcgPSBpZmVsc2UoZml0bmVzc19nbGIgPiBmaXRuZXNzX2xjLCBwYXI3X2dsYiwgcGFyN19sYyksCiAgICBwYXI4ID0gaWZlbHNlKGZpdG5lc3NfZ2xiID4gZml0bmVzc19sYywgcGFyOF9nbGIsIHBhcjhfbGMpLAogICAgcGFyOSA9IGlmZWxzZShmaXRuZXNzX2dsYiA+IGZpdG5lc3NfbGMsIHBhcjlfZ2xiLCBwYXI5X2xjKQogICkgIyMgdGhlIGNvbHVtbnMgYXJlIGFzc2lnbmVkIGJhc2VkIG9uIHdoaWNoIHN0cmF0ZWd5IHByb2R1Y2VkIHRoZSBoaWdoZXN0IGZpdG5lc3MKCiMjIHdyaXRlIGNzdgp3cml0ZS5jc3YoZHVhbF9jdWVfZl9maW5hbC5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9jdWVfZml0bmVzc19maW5hbC5jc3YiKSkKYGBgCgojIyBDb21iaW5lIHNpbmdsZSBjdWUgYW5kIGR1YWwgY3VlIGRhdGFzZXQKYGBge3J9CmR1YWxfY3VlX2ZfZmluYWwuZGYgPC0gcmVhZC5jc3YoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9jdWVfZml0bmVzc19maW5hbC5jc3YiKSkKCmR1YWxfc2lfZml0bmVzcy5kZiA8LSBkdWFsX2N1ZV9mX2ZpbmFsLmRmICU+JSAKICBzZWxlY3QoaWQsIGlkX2IsIGxhYmVsLCBsYWJlbF9iLCBmaXRuZXNzX2R1YWwgPSBmaXRuZXNzKSAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChzaV9vcHQuZGYsIGlkLCBmaXRuZXNzX3NpID0gZml0bmVzc18yMCksIGJ5ID0gImlkIikgJT4lICMjIGdldCBmaXRuZXNzIG9mIHNpbmdsZSBjdWUgbW9kZWwgYmFzZWQgb24gZmlyc3QgY3VlCiAgbGVmdF9qb2luKHNlbGVjdChzaV9vcHQuZGYsIGlkX2IgPSBpZCwgZml0bmVzc19zaV9iID0gZml0bmVzc18yMCksIGJ5ID0gImlkX2IiKSAlPiUgICMjIGdldCBmaXRuZXNzIG9mIHNpbmdsZSBjdWUgbW9kZWwgYmFzZWQgb24gc2Vjb25kIGN1ZQogIG11dGF0ZShmaXRuZXNzX3NpX2ZpbmFsID0gaWZlbHNlKGZpdG5lc3Nfc2kgPiBmaXRuZXNzX3NpX2IsIGZpdG5lc3Nfc2ksIGZpdG5lc3Nfc2lfYikvOS44ODM2MDIsCiAgICAgICAgIGxhYmVsX2NvbWIgPSBwYXN0ZShsYWJlbCwgIiYiLCBsYWJlbF9iKSwgIyMgc2VsZWN0IHRoZSBoaWdoZXN0IHNpbmdsZSBjdWUgbW9kZWwgZml0bmVzcwogICAgICAgICBmaXRuZXNzX2R1YWxfbm9ybSA9IGZpdG5lc3NfZHVhbC85Ljg4MzYwMikjIyBub3JtYWxpemUgYnkgdGltZSBmaXRuZXNzCmBgYAoKIyMgcGxvdCBmaXRuZXNzIHJhbmsKYGBge3J9CmR1YWxfZml0bmVzcy5wbCA8LSBnZ3Bsb3QoZHVhbF9zaV9maXRuZXNzLmRmKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh5ID0gZmN0X3Jlb3JkZXIobGFiZWxfY29tYiwgZml0bmVzc19kdWFsX25vcm0pLCB5ZW5kID0gZmN0X3Jlb3JkZXIobGFiZWxfY29tYiwgZml0bmVzc19kdWFsKSwgeCA9IGZpdG5lc3NfZHVhbF9ub3JtLCB4ZW5kID0gZml0bmVzc19zaV9maW5hbCkpICsgIyMgbGluZSBzZWdtZW50IGNvbm5lY3RpbmcgdGhlIGN1ZSB2YWx1ZXMKICBnZW9tX3BvaW50KGFlcyh5ID0gZmN0X3Jlb3JkZXIobGFiZWxfY29tYiwgZml0bmVzc19kdWFsKSwgeCA9IGZpdG5lc3NfZHVhbF9ub3JtLCBjb2xvciA9ICJEdWFsIGN1ZSIsIHNoYXBlID0gIkR1YWwgY3VlIiksCiAgICAgICAgICAgICBzaXplID0gMi41KSArICMjIGR1YWwgY3VlIGZpdG5lc3MKICBnZW9tX3BvaW50KGFlcyh5ID0gZmN0X3Jlb3JkZXIobGFiZWxfY29tYiwgZml0bmVzc19kdWFsX25vcm0pLCB4ID0gZml0bmVzc19zaV9maW5hbCwgY29sb3IgPSAiQmVzdCBzaW5nbGUgY3VlIiwgc2hhcGUgPSAiQmVzdCBzaW5nbGUgY3VlIiksIHNpemUgPSAyLjUpICsgIyMgYmVzdCBzaW5nbGUgY3VlIGZpdG5lc3MKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiNmYzhkNTkiLCAiIzQ1NzViNCIpKSArCiAgbGFicyh4ID0gIk5vcm1hbGl6ZWQgZml0bmVzcyIsIHkgPSAiRHVhbCBjdWUgY29tYmluYXRpb25zIiwgY29sb3VyID0gIkxlZ2VuZCIsIHNoYXBlID0gIkxlZ2VuZCIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMtLS0tLS0tLS0tLS1UaW1lIHNlcmllcyBjb252ZXJzaW9uIHJhdGUgb2YgZHVhbCBjdWUgbW9kZWxzLS0tLS0tLS0tLS0jCk5vdGUgdGhhdCBvdXIgZHVhbCBjdWUgbW9kZWxzIHRha2UgOSBwYXJhbWV0ZXJzLiBUbyBlbnN1cmUgdGhhdCBhbnkgZGlmZmVyZW5jZSAgYmV0d2VlbiBkdWFsIGFuZCBzaW5nbGUgY3VlIG1vZGVscyBpcyBkdWUgdG8gdGhlIGluY2x1c2lvbiBvZiBhZGRpdGlvbmFsIGN1ZXMgYW5kIE5PVCBoaWdoZXIgc3BsaW5lIGZsZXhpYmlsaXR5LHdlIHBlcmZvcm1lZCBMLUJGR1MtQiAobG9jYWwpIG9wdGltaXphdGlvbiBvZiBzaW5nbGUgY3VlIG1vZGVscyB3aXRoIGRmID0gOSB0byBjb250cm9sIGZvciB0aGUgc3BsaW5lIGZsZXhpYmlsaXR5LiAKCiMjIGR5bmFtaWNzIHNpbXVsYXRpb24gb2YgaGlnaCBwYXJhbWV0ZXIgY3VlcyAKYGBge3J9CiMjIGJlc3QgZHVhbCBjdWUgbW9kZWw6IEkgbG9nIGFuZCBSIGxvZwpSbG9nX0lsb2cuY3IgPC0gY2hhYmF1ZGlfc2lfY2xlYW4oCiAgcGFyYW1ldGVyc19jciA9IGMoNC40NDYxOTIwMzMsCTEwLjk3NTE4Mjc1LAkxLjM4NzYyODE3LAkyMy4zMDU5MjU0LAktMy40NTIwNTIzNzEsCS0xOC4wMDcwNjkyLAkzOS42NjYxNDIyNiwJLTMuNTQ1MTkzMTQxLAkxOC43ODM1MDc5OSksCiAgaW1tdW5pdHkgPSAidHN1a3VzaGkiLAogIHBhcmFtZXRlcnMgPSBwYXJhbWV0ZXJzX3RzdWt1c2hpLAogIHRpbWVfcmFuZ2UgPSBzZXEoMCwgMjAsIGJ5ID0gMWUtMyksCiAgY3VlX3JhbmdlID0gIHNlcSg2LCA3LCBieSA9IDEvNTAwKSwKICBjdWVfcmFuZ2VfYiA9IHNlcSgwLCBsb2cxMCg2KigxMF42KSksIGJ5ID0gKGxvZzEwKDYqKDEwXjYpKSkvNTAwKSwKICBjdWUgPSAiUiIsCiAgY3VlX2IgPSAiSSIsCiAgbG9nX2N1ZSA9ICJsb2cxMCIsCiAgbG9nX2N1ZV9iID0gImxvZzEwIiwKICBzb2x2ZXIgPSAidm9kZSIsCiAgZHluID0gVAopCgojIyB3aGVuIHRpbWUgaXMgdXNlZCBhcyBhIGN1ZSAoOSBwYXJhbWV0ZXJzKS4gY2hhYmF1ZGlfc2lfY2xlYW5faGlnaCBpcyBzaW1wbHkgYSB2YXJpYXRpb24gb2YgY2hhYmF1ZGlfc2lfY2xlYW4gdGhhdCBtYWtlcyBhIG1vcmUgZmxleGlibGUgc3BsaW5lIQp0aW1lX2hpZ2guY3IgPC0gY2hhYmF1ZGlfc2lfY2xlYW5faGlnaCgKICBwYXJhbWV0ZXJzX2NyID0gYyg5LjE1NDMxNCwgIC03LjU3MDgyOSwgLTIyLjUwNjYzOCAsICAzLjM4MjQwNSAsLTEzLjQ1MzUxOSAsLTE3LjAxMTQ4NSAgLCAzLjY3ODE4MSwgLTEyLjg1MTg5NSAsLTI2LjExNTE1OCksCiAgaW1tdW5pdHkgPSAidHN1a3VzaGkiLAogIHBhcmFtZXRlcnMgPSBwYXJhbWV0ZXJzX3RzdWt1c2hpLAogIHRpbWVfcmFuZ2UgPSBzZXEoMCwgMjAsIGJ5ID0gMWUtMyksCiAgY3VlX3JhbmdlID0gIHNlcSgwLCAyMCwgYnkgPSAxZS0zKSwKICBjdWUgPSAidCIsCiAgc29sdmVyID0gInZvZGUiLAogIGR5biA9IFQpCgojIyB3aGVuIGFzZXh1YWwgaVJCQyBpcyB1c2VkIGFzIGEgY3VlIChoaWdoIGZsZXhpYmlsaXR5KQpJX2hpZ2guY3IgPC0gY2hhYmF1ZGlfc2lfY2xlYW5faGlnaCgKICBwYXJhbWV0ZXJzX2NyID0gYygxLjI5NjY3NSwgIDMuNTQ0MDM0ICwgNC45MDc0ODQsICAyLjE3NDI0OSwgLTMuMjM4MzA5ICwtNS4xODE2MTQgLC0xLjY0NTA3MiAsIDEuODM0MzAyICwgMS41ODEwMTEpLAogIGltbXVuaXR5ID0gInRzdWt1c2hpIiwKICBwYXJhbWV0ZXJzID0gcGFyYW1ldGVyc190c3VrdXNoaSwKICB0aW1lX3JhbmdlID0gc2VxKDAsIDIwLCBieSA9IDFlLTMpLAogIGN1ZV9yYW5nZSA9ICBzZXEoMCwgbG9nMTAoNiooMTBeNikpLCBieSA9IChsb2cxMCg2KigxMF42KSkpLzUwMDApLAogIGN1ZSA9ICJJIiwKICBsb2dfY3VlID0gImxvZzEwIiwKICBzb2x2ZXIgPSAidm9kZSIsCiAgZHluID0gVCkKCiMjIHdoZW4gUkJDIGlzIHVzZWQgYXMgY3VlIChoaWdoIGZsZXhpYmlsaXR5KQpSX2hpZ2guY3IgPC0gY2hhYmF1ZGlfc2lfY2xlYW5faGlnaCgKICBwYXJhbWV0ZXJzX2NyID0gYyg1LjAzNDAzNDggLCAgMC41ODQ2MTY4ICwgIDAuMzc0OTY0OCAsICAwLjY4NDI2NzMgICwgMi40NzQ4MTA3ICwgMTAuOTAzNjAzNCAsIDE2LjgyNDYzMTYsIC0yNC44NjkwOTcxICwxLjgwMDcyMzgpLAogIGltbXVuaXR5ID0gInRzdWt1c2hpIiwKICBwYXJhbWV0ZXJzID0gcGFyYW1ldGVyc190c3VrdXNoaSwKICB0aW1lX3JhbmdlID0gc2VxKDAsIDIwLCBieSA9IDFlLTMpLAogIGN1ZV9yYW5nZSA9ICBzZXEobG9nMTAoMTBeNiksIGxvZzEwKDEwXjcpLCBieSA9IChsb2cxMCgxMF43KS1sb2cxMCgxMF42KSkvNTAwMCksCiAgY3VlID0gIlIiLAogIGxvZ19jdWUgPSAibG9nMTAiLAogIHNvbHZlciA9ICJ2b2RlIiwKICBkeW4gPSBUKQoKIyBwcm9jZXNzIApJX2hpZ2guY3JfcCA8LSBJX2hpZ2guY3IgJT4lIGZpbHRlcih2YXJpYWJsZSA9PSAiY3IiKSAlPiUgbXV0YXRlKGxhYmVsX25ldyA9ICJJIGxvZzEwIChkZj05KSIpICU+JSBzZWxlY3QoLXZhcmlhYmxlKQoKUl9oaWdoLmNyX3AgPC0gUl9oaWdoLmNyICU+JSBmaWx0ZXIodmFyaWFibGUgPT0gImNyIikgJT4lIG11dGF0ZShsYWJlbF9uZXcgPSAiUiBsb2cxMCAoZGY9OSkiKSAlPiUgc2VsZWN0KC12YXJpYWJsZSkKCnRpbWVfaGlnaC5jcl9wIDwtIHRpbWVfaGlnaC5jciAlPiUgZmlsdGVyKHZhcmlhYmxlID09ICJjciIpICU+JSBtdXRhdGUobGFiZWxfbmV3ID0gIlRpbWUgKGRmPTkpIikgJT4lIHNlbGVjdCgtdmFyaWFibGUpCgpSbG9nX0lsb2cuY3JfcCA8LSBSbG9nX0lsb2cuY3IgJT4lIGZpbHRlcih2YXJpYWJsZSA9PSAiY3IiKSAlPiUgbXV0YXRlKGxhYmVsX25ldyA9ICJSIGxvZzEwICYgSSBsb2cxMFxuKGRmPTkpIikgJT4lIHNlbGVjdCgtdmFyaWFibGUpCgojIyBjb21iaW5lCmR1YWxfc2VsZWN0ZWRfY3IuZGYgPC0gcmJpbmQoSV9oaWdoLmNyX3AsIFJfaGlnaC5jcl9wLCB0aW1lX2hpZ2guY3JfcCwgUmxvZ19JbG9nLmNyX3ApCndyaXRlX3BhcnF1ZXQoZHVhbF9zZWxlY3RlZF9jci5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9zZWxlY3RlZF9jci5wYXJxdWV0IikpCmBgYAoKIyMgcGxvdApgYGB7cn0KZHVhbF9zZWxlY3RlZF9jci5kZiA8LSByZWFkX3BhcnF1ZXQoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9zZWxlY3RlZF9jci5wYXJxdWV0IikpCgpkdWFsX3NlbGVjdGVkX2NyLnBsIDwtIGdncGxvdCgpICsKICBnZW9tX2xpbmUoZGF0YSA9IGR1YWxfc2VsZWN0ZWRfY3IuZGYsIGFlcyhjb2xvciA9IGxhYmVsX25ldywgeCA9IHRpbWUsIHkgPSB2YWx1ZSksIHNpemUgPSAxKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZHVhbF9zZWxlY3RlZF9jci5kZiAlPiUgZmlsdGVyKHRpbWUlJTEgPT0gMCksIGFlcyhjb2xvciA9IGxhYmVsX25ldywgeCA9IHRpbWUsIHkgPSB2YWx1ZSwgc2hhcGUgPSBsYWJlbF9uZXcpLCBzaXplID0gMykgKwogIGxhYnMoeCA9ICJUaW1lIChkYXlzKSIsIHkgPSAiQ29udmVyc2lvbiByYXRlIiwgY29sb3IgPSAiQ3VlKHMpIiwgc2hhcGUgPSAiQ3VlKHMpIikgKwogIHhsaW0oMCwgMjApICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiI2ZjOGQ1OSIsIiNmZGNiNDQiLCJibGFjayIsICIjNDU3NWI0IikpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiLAogICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKHQgPSA0MCwgciA9IDAsIGIgPSAwLCBsID0gMCwgdW5pdCA9ICJwdCIpKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSA0LCBieXJvdyA9IFRSVUUpKQpgYGAKCiMtLS0tLS0tLSBSZWFjdGlvbiBub3JtIGhlYXRtYXAgb2YgUiBsb2cxMCArIEkgbG9nMTAgLS0tLS0tLS0tLS0tIwojIFByb2Nlc3MgZGF0YQpgYGB7cn0KIyBtYWtlIGhlYXRtYXAgZGF0YS5mcmFtZQpSbG9nX0lsb2cuaG0gPC0gcGFyX3RvX2htX3RlKHBhciA9IGMoNC40NDYxOTIwMzMsCTEwLjk3NTE4Mjc1LAkxLjM4NzYyODE3LAkyMy4zMDU5MjU0LAktMy40NTIwNTIzNzEsCS0xOC4wMDcwNjkyLAkzOS42NjYxNDIyNiwJLTMuNTQ1MTkzMTQxLAkxOC43ODM1MDc5OSksCiAgICAgICAgICAgICBjdWVfcmFuZ2UgPSBzZXEoNiwJNywgbGVuZ3RoLm91dCA9IDUwMCksCiAgICAgICAgICAgICBjdWVfcmFuZ2VfYiA9IHNlcSgwLAk2Ljc3ODE1MTI1LCBsZW5ndGgub3V0ID0gNTAwKSkKCiMgcHJvY2VzcyBkeW5hbWljcwpSbG9nX0lsb2cuZHluIDwtIFJsb2dfSWxvZy5jciAlPiUgCiAgdGlkeXI6OnBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB2YXJpYWJsZSwgdmFsdWVzX2Zyb20gPSB2YWx1ZSkgJT4lIAogIG11dGF0ZShsb2dfUiA9IGxvZzEwKFIpLAogICAgICAgICBsb2dfSSA9IGxvZzEwKEkpKQpgYGAKCiMgcGxvdApgYGB7cn0KUmxvZ19JbG9nX3JuLnBsIDwtIGdncGxvdCgpICsKICBnZW9tX3Jhc3RlcihkYXRhID0gUmxvZ19JbG9nLmhtLCBhZXMoeCA9IGN1ZV9yYW5nZV9iLCB5ID0gY3VlX3JhbmdlLCBmaWxsID0gY3IpKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBSbG9nX0lsb2cuZHluLCBhZXMoeCA9IGxvZ19JLCB5ID0gbG9nX1IpLCBjb2xvciA9ICJ3aGl0ZSIsIGFycm93ID0gYXJyb3coYW5nbGUgPSAzMCwgbGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKSkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBSbG9nX0lsb2cuZHluICU+JSBmaWx0ZXIocm93X251bWJlcigpICUlIDEwMDAgPT0gMSAmIHRpbWUgPD0gMjApLCBhZXMoeCA9IGxvZ19JLCB5ID0gbG9nX1IpLCBjb2xvciA9ICJ3aGl0ZSIpICsKICB4bGltKDAuOTkqbWluKGhhYmxhcjo6cyhSbG9nX0lsb2cuZHluJGxvZ19JKSwgbmEucm0gPSBUKSwgMS4wMSogbWF4KGhhYmxhcjo6cyhSbG9nX0lsb2cuZHluJGxvZ19JKSwgbmEucm0gPSBUKSkgKwogIHlsaW0oMC45OSptaW4oaGFibGFyOjpzKFJsb2dfSWxvZy5keW4kbG9nX1IpLCBuYS5ybSA9IFQpLDEuMDEqIG1heChoYWJsYXI6OnMoUmxvZ19JbG9nLmR5biRsb2dfUiksIG5hLnJtID0gVCkpICsKICBsYWJzKHkgPSAiUkJDIGxvZzEwIiwgeCA9ICJBc2V4dWFsIGlSQkMgbG9nMTAiLCBmaWxsID0gIkNvbnZlcnNpb25cbnJhdGUiKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMtLS0tLS0tLS0tLSBQbG90IHRvZ2V0aGVyIC0tLS0tLS0tLS0tLSMKYGBge3J9CiMgYXNzZW1ibGUgcGFuZWwgQiBhbmQgQwpkdWFsX21haW4uQkMgPC0gZ2dhcnJhbmdlKGR1YWxfc2VsZWN0ZWRfY3IucGwsIFJsb2dfSWxvZ19ybi5wbCwgYWxpZ24gPSAidiIsIG5jb2wgPSAxLCBsYWJlbHMgPSBjKCJCIiwgIkMiKSkKCiMgYXNzZW1ibGUgcGFuZWwgQQpnZ2FycmFuZ2UoZHVhbF9maXRuZXNzLnBsLCBkdWFsX21haW4uQkMsIG5jb2wgPSAyLCBsYWJlbHMgPSBjKCJBIiwgIiIpLCB3aWR0aHMgPSBjKDEuMSwxKSkKZ2dzYXZlKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9maWd1cmVzL2R1YWxfbWFpbl9pbnRlcm1lZGlhdGUudGlmZiIpLCB1bml0cyA9ICJweCIsIHdpZHRoID0gMjI1MCwgaGVpZ2h0ID0gMTQwMCwgc2NhbGUgPSAxLjMsIGRwaT0zMDAsICBiZyA9ICJ3aGl0ZSIpCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIyBEdWFsIGN1ZSBjb252ZXJzaW9uIHJhdGUgc3VwcGxlbWVudGFyeSBmaWd1cmUKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIyMgRnVuY3Rpb24gdG8gc2ltdWxhdGUgZHVhbCBjdWUgZHluYW1pY3MKYGBge3J9CmR1YWxfY3VlX2R5biA8LSBmdW5jdGlvbihkZil7CiAgIyMgcHJvY2VzcyBjdWVzCiAgY3VlIDwtIGRmJGN1ZQogIGN1ZV9iIDwtIGRmJGN1ZV9iCiAgCiAgIyMgcHJvY2VzcyBsb2cKICBsb2cgPC0gaWZlbHNlKHN0cl9kZXRlY3QoZGYkaWQsICJsb2ciKSwgImxvZzEwIiwgIm5vbmUiKQogIGxvZ19iIDwtIGlmZWxzZShzdHJfZGV0ZWN0KGRmJGlkX2IsICJsb2ciKSwgImxvZzEwIiwgIm5vbmUiKQogIAogICMgcHJvY2VzcyBjdWVfcmFuZ2UuIGVuc3VyZSB0aGF0IGJvdGggY3VlIHJhbmdlcyBhcmUgb2YgdGhlIHNhbWUgbGVuZ3RoCiAgY3VlX3JhbmdlIDwtIHNlcShkZiRsb3csIGRmJGhpZ2gsIGxlbmd0aC5vdXQgPSA1MDApCiAgY3VlX3JhbmdlX2IgPC0gc2VxKGRmJGxvd19iLCBkZiRoaWdoX2IsIGxlbmd0aC5vdXQgPSA1MDApCiAgCiAgIyBnZXQgcGFyYW1ldGVyIHNldAogIHBhciA8LSBjKGRmJHBhcjEsIGRmJHBhcjIsIGRmJHBhcjMsIGRmJHBhcjQsIGRmJHBhcjUsIGRmJHBhcjYsIGRmJHBhcjcsIGRmJHBhcjgsIGRmJHBhcjkpCiAgCiAgIyBzaW11bGF0ZSBkeW5hbWljcwogIGR5biA8LSBjaGFiYXVkaV9zaV9jbGVhbigKICAgIHBhcmFtZXRlcnNfY3IgPSBwYXIsCiAgICBpbW11bml0eSA9ICJ0c3VrdXNoaSIsCiAgICBwYXJhbWV0ZXJzID0gcGFyYW1ldGVyc190c3VrdXNoaSwKICAgIHRpbWVfcmFuZ2UgPSBzZXEoMCwgMjAsIDAuMDEpLAogICAgY3VlID0gY3VlLAogICAgY3VlX2IgPSBjdWVfYiwKICAgIGN1ZV9yYW5nZSA9IGN1ZV9yYW5nZSwKICAgIGN1ZV9yYW5nZV9iID0gY3VlX3JhbmdlX2IsCiAgICBsb2dfY3VlID0gbG9nLAogICAgbG9nX2N1ZV9iID0gbG9nX2IsCiAgICBzb2x2ZXIgPSAidm9kZSIsCiAgICBnYW0gPSAidGUiLAogICAgZHluID0gVCkKICAKICAjIHJldHVybiAKICBkeW4yIDwtIGNiaW5kKGlkID0gZGYkaWQsIGlkX2IgPSBkZiRpZF9iLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gZGYkbGFiZWwsIGxhYmVsX2IgPSBkZiRsYWJlbF9iLAogICAgICAgICAgICAgICAgY3VlID0gY3VlLCBjdWVfYiA9IGN1ZV9iLCBkeW4pCiAgCiAgd3JpdGVfcGFycXVldChkeW4yLCBoZXJlKHBhc3RlMCgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9jdWVfZHluLyIsIGRmJGlkLCAiXyIsIGRmJGlkX2IsICIucGFycXVldCIpKSkKfQpgYGAKCiMjIEdldCBkeW5hbWljcyBvZiBkdWFsIGN1ZSBtb2RlbHMKYGBge3J9CiMjIEZvciB0aGUgZHVhbCBjdWUgZml0bmVzcyBkYXRhZnJhbWUsIGFkZCB0aGUgY3VlIHJhbmdlIGZvciBlYWNoIGluZGl2aWR1YWwgY3VlCmR1YWxfY3VlX2ZfZmluYWwuZGZfcCA8LSBkdWFsX2N1ZV9mX2ZpbmFsLmRmICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KGN1ZV9yYW5nZV9zaV9hbHQuZGYsIGlkLCBjdWUsIGxvdywgaGlnaCksIGJ5ID0gImlkIikgJT4lIAogIGxlZnRfam9pbihzZWxlY3QoY3VlX3JhbmdlX3NpX2FsdC5kZiwgaWRfYiA9IGlkLCBjdWVfYiA9IGN1ZSwgbG93X2IgPSBsb3csIGhpZ2hfYiA9IGhpZ2gpLCBieSA9ICJpZF9iIikKCiMjIFNwbGl0IGRhdGFmcmFtZXMKZHVhbF9jdWVfZl9maW5hbC5scyA8LSBzcGxpdChkdWFsX2N1ZV9mX2ZpbmFsLmRmX3AsIHNlcShucm93KGR1YWxfY3VlX2ZfZmluYWwuZGZfcCkpKQoKIyMgUnVuIGZ1bmN0aW9uCm1jbGFwcGx5KGR1YWxfY3VlX2ZfZmluYWwubHMsIGR1YWxfY3VlX2R5biwgbWMuY29yZXMgPSA2KQoKIyMgQ29uY2F0IGFsbCBmaWxlcwpkdWFsX2N1ZV9keW4ubHMgPC0gbGlzdC5maWxlcyhwYXRoID0gaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9jdWVfZHluIiksIHBhdHRlcm4gPSAiKi5wYXJxdWV0IiwgZnVsbC5uYW1lcyA9IFQpCmR1YWxfY3VlX2R5bi5kZiA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkoZHVhbF9jdWVfZHluLmxzLCByZWFkX3BhcnF1ZXQpKQp3cml0ZV9wYXJxdWV0KGR1YWxfY3VlX2R5bi5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9jdWVfZHluLnBhcnF1ZXQiKSkKYGBgCgojIyBnZXQgMzAgZGF5cyBkeW5hbWljIChmb3IgcGxvdHRpbmcgcHVycG9zZXMpCmBgYHtyfQojIyBSdW4gZnVuY3Rpb24KbWNsYXBwbHkoZHVhbF9jdWVfZl9maW5hbC5scywgZHVhbF9jdWVfZHluLCBtYy5jb3JlcyA9IDYpCgojIyBDb25jYXQgYWxsIGZpbGVzCmR1YWxfY3VlX2R5bl8zMC5scyA8LSBsaXN0LmZpbGVzKHBhdGggPSBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX2N1ZV9keW5fMzAiKSwgcGF0dGVybiA9ICIqLnBhcnF1ZXQiLCBmdWxsLm5hbWVzID0gVCkKZHVhbF9jdWVfZHluXzMwLmRmIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseShkdWFsX2N1ZV9keW5fMzAubHMsIHJlYWRfcGFycXVldCkpCndyaXRlX3BhcnF1ZXQoZHVhbF9jdWVfZHluXzMwLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX2N1ZV9keW5fMzAucGFycXVldCIpKQpgYGAKCiMjIFByZXBhcmluZyBkYXRhc2V0IGZvciBwbG90dGluZwpgYGB7cn0KIyMgZmlsdGVyIG91dCBvbmx5IGNvbnZlcnNpb24gcmF0ZSBhbmQgYXR0YWNoIGZpdG5lc3MgdmFsdWVzCmR1YWxfY3VlX2NyLmRmIDwtIGR1YWxfY3VlX2R5bi5kZiAlPiUgCiAgZmlsdGVyKHZhcmlhYmxlID09ICJjciIpICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KGR1YWxfY3VlX2ZfZmluYWwuZGYsIGlkLCBpZF9iLCBmaXRuZXNzKSwgYnkgPSBjKCJpZCIsICJpZF9iIikpICU+JSAKICBtdXRhdGUobGFiZWxfY29tYiA9IHBhc3RlKGxhYmVsLCAiJiIsIGxhYmVsX2IpKQoKIyMgU2FuaXR5IGNoZWNrIHRoYXQgdGhlIGR5bmFtaWNzIHByb2R1Y2VkIHRoZSBzYW1lIGZpdG5lc3MgYXMgdGhlIG9wdGltaXplZCB2YWx1ZXMuIFllcyEKZHVhbF9jdWVfZHluLmRmICU+JSAKICBmaWx0ZXIodmFyaWFibGUgPT0gInRhdV9jdW0iKSAlPiUgCiAgZmlsdGVyKHRpbWUgPT0gMjApICU+JSAKICBkaXN0aW5jdChpZCwgaWRfYiwgdmFsdWUpICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KGR1YWxfY3VlX2ZfZmluYWwuZGYsIGlkLCBpZF9iLCBmaXRuZXNzKSwgYnkgPSBjKCJpZCIsICJpZF9iIikpICU+JSAKICBtdXRhdGUoZGlmZiA9IHZhbHVlLWZpdG5lc3MpCmBgYAoKIyMgUGxvdApgYGB7cn0KZ2dwbG90KGRhdGEgPSBkdWFsX2N1ZV9jci5kZiwgYWVzKHggPSB0aW1lLCB5ID0gZmN0X3Jlb3JkZXIobGFiZWxfY29tYiwgZml0bmVzcyksIGZpbGwgPSB2YWx1ZSkpICsKICBnZW9tX3Jhc3RlcigpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsKICB4bGltKDEsIDIwKSArCiAgbGFicyh4ID0gIlRpbWUgKGRheXMpIiwgeSA9ICJEdWFsIGN1ZSBjb21iaW5hdGlvbiIsIGZpbGwgPSAiQ29udmVyc2lvbiByYXRlIikgKwogIHRoZW1lX2NsYXNzaWMoKQoKZ2dzYXZlKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9maWd1cmVzL2R1YWxfY3VlX2NyLnRpZmYiKSwgdW5pdHMgPSAicHgiLCB3aWR0aCA9IDIwMDAsIGhlaWdodCA9IDE1MDAsIGRwaT0zMDAsICBiZyA9ICJ3aGl0ZSIpCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIyBFeHBlcmltZW50YWwgZGlzZWFzZSBtYXBzIG9mIFAuIGNoYWJhdWRpCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMjIEltcG9ydCBpbiBkYXRhCmBgYHtyfQojIGltcG9ydCBpbiBodHRwczovL2FjYWRlbWljLm91cC5jb20vZW1waC9hcnRpY2xlLzIwMTgvMS8xMjcvNTA0NTg3MT9sb2dpbj10cnVlCiMjICgyMDE4IHB1Ymxpc2hlZCBpbiBFTVBIKQplbXBoXzIwMTggPC0gcmVhZHhsOjpyZWFkX3hscyhoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZXhwZXJpbWVudGFsX2RhdGEvSHVpamJlbl8yMDE4X0VNUEgueGxzIiksIHNoZWV0ID0gMSkKCiMgaW1wb3J0IGluIGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pLzEwLjExMTEvai4xNTU4LTU2NDYuMjAxMC4wMTA2OC54CiMjICgyMDEwIHB1Ymxpc2hlZCBpbiBFdm9sdXRpb24pCmV2b18yMDEwIDwtIHJlYWR4bDo6cmVhZF94bHMoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2V4cGVyaW1lbnRhbF9kYXRhL0h1aWpiZW5fMjAxMF9ldm9sdXRpb24ueGxzIiksIHNoZWV0ID0gMSkKCiMgaW1wb3J0IGluIGh0dHBzOi8vam91cm5hbHMucGxvcy5vcmcvcGxvc3BhdGhvZ2Vucy9hcnRpY2xlP2lkPTEwLjEzNzEvam91cm5hbC5wcGF0LjEwMDM1NzgjOn46dGV4dD1UaGUlMjBwaGlsb3NvcGh5JTIwaXMlMjB0aGF0JTIwYWdncmVzc2l2ZSxsb25nZXIlMjBmZWVsJTIwc2ljayUyMCU1QjEzJTVELgojIyAoMjAxMyBpbiBQTG9TIHBhdGhvZ2VuKQpwbG9zXzIwMTNfMSA8LSByZWFkeGw6OnJlYWRfeGxzeChoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZXhwZXJpbWVudGFsX2RhdGEvSHVpamJlbl8yMDEzX1BMb1MueGxzeCIpLCBzaGVldCA9IDIpCgpwbG9zXzIwMTNfMiA8LSByZWFkeGw6OnJlYWRfeGxzeChoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZXhwZXJpbWVudGFsX2RhdGEvSHVpamJlbl8yMDEzX1BMb1MueGxzeCIpLCBzaGVldCA9IDMpCgojIGltcG9ydCBpbiBodHRwczovL29ubGluZWxpYnJhcnkud2lsZXkuY29tL2RvaS8xMC4xMTExL2ouMTQyMC05MTAxLjIwMTEuMDIzNjkueAojIyAoMjAxMSBpbiBKb3VybmFsIG9mIEV2b2x1dGlvbmFyeSBCaW9sb2d5KQplc2ViXzIwMTEgPC0gcmVhZHhsOjpyZWFkX3hscyhoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZXhwZXJpbWVudGFsX2RhdGEvSHVpamJlbl8yMDExX2VzZWIueGxzIiksIHNoZWV0ID0gMSkKCiMgaW1wb3J0IGluIGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzM5MzkzNTEvCiMjICgyMDExIGluIEpvdXJuYWwgb2YgQW1lcmljYW4gbmF0dXJhbGlzdCkuIE5vdGUgcHJpdmF0ZSBkYXRhc2V0IHNvIG5vdCBwcm92aWRlZCBpbiB0aGUgc3VwcGxlbWVudGFyeSEKYW1uYV8yMDExIDwtIHJlYWR4bDo6cmVhZF94bHMoaGVyZSgiZXhwZXJpbWVudGFsX2RhdGEvUG9sbGl0dF8yMDExX25hdHVyYWxpc3QueGxzIiksIHNoZWV0ID0gMSkKYGBgCgojIyBDbGVhbiBkYXRhIApgYGB7cn0KIyMgZm9yIEVNUEggMjAxOCBzdHVkeSwgaW5jbHVkZSBvbmx5IGluZmVjdGlvbiBzZXJpZXMgd2l0aG91dCBkcnVncyB3ZXJlIFItaW5vY3VsdW0gaXMgYWRtaW5pc3RlcmVkIGJ5IGl0c2VsZiwgd2hpY2ggaW5jbHVkZXMgNiwgNywgOCwgOSwgMTAuIEJveCA2IGhhcyBhIHN0YXJ0aW5nIGlub2N1bHVtIG51bWJlciBvZiAxMF42LCB3aGljaCBpcyBtb3N0IHNpbWlsYXIgdG8gb3RoZXIgc3R1ZGllcy4gRmlsdGVyaW5nIGJldHdlZW4gZGF5IDMtMjEgYmVjYXVzZSB0aG9zZSBhcmUgdGhlIGRheXMgd2hlcmUgd2UgaGF2ZSBzaW5nbGUgZGF5IGRhdGEuCmVtcGhfMjAxOF9zcy5kZiA8LSBlbXBoXzIwMTggJT4lIAogIGZpbHRlcihCb3ggJWluJSBzZXEoNiwgMTApICYKICAgICAgICAgZHBseXI6OmJldHdlZW4oRGF5LCAzLCAyMSkpICU+JSAKICBtdXRhdGUoZG9zZSA9IGNhc2Vfd2hlbigKICAgIEJveCA9PSA2IH4gMTBeNiwKICAgIEJveCA9PSA3IH4gMTBeNSwKICAgIEJveCA9PSA4IH4gMTBeMywKICAgIEJveCA9PSA5IHwgQm94ID09IDEwIH4gMTBeMQogICkpICU+JSAKICBtdXRhdGUoc3RyYWluID0gIkFzNnAiLAogICAgICAgICBzdHVkeSA9ICJlbXBoMjAxOCIsCiAgICAgICAgIHN0dWR5X3N0cmFpbiA9IHBhc3RlMChzdHJhaW4sIHN0dWR5KSwKICAgICAgICAgaWQgPSBwYXN0ZTAoc3R1ZHksIHN0cmFpbiwgQm94LCBNb3VzZSwgMSksCiAgICAgICAgIFJCQyA9IFJCQyAqICgxMF42KSkgJT4lCiAgc2VsZWN0KGRheSA9IERheSwKICAgICAgICAgbW91c2UgPSBNb3VzZSwgCiAgICAgICAgIFJCQywgCiAgICAgICAgIGFzZXggPSBSYXNleCwKICAgICAgICAgZ2FtID0gUmdhbSwKICAgICAgICAgZG9zZSwKICAgICAgICAgc3RyYWluLAogICAgICAgICBzdHVkeSwKICAgICAgICAgc3R1ZHlfc3RyYWluLAogICAgICAgICBpZCkKCiMjIGZvciAyMDExIGVzZWIsIG9ubHkgZGF5IDMtMTcgZGF0YSBhcmUgYW5hbHl6ZWQgYmVjYXVzZSB0aG9zZSBhcmUgdGhlIGRheXMgd2hlcmUgZ2FtZXRvY3l0ZSBkYXRhIGFyZSBhdmFpbGFibGUKZXNlYl8yMDExX3NzLmRmIDwtIGVzZWJfMjAxMSAlPiUgCiAgZmlsdGVyKENsb25lcyA9PSAiUiIgJiBiZXR3ZWVuKERheSwgMywgMTcpICYKICAgICAgICAgICBEcnVncyA9PSAiTiIpICU+JSAKICBtdXRhdGUoZG9zZSA9IDEwXjYsCiAgICAgICAgIHN0cmFpbiA9ICJBczhwIiwKICAgICAgICAgc3R1ZHkgPSAiZXNlYjIwMTEiLAogICAgICAgICBzdHVkeV9zdHJhaW4gPSBwYXN0ZTAoc3RyYWluLCBzdHVkeSksCiAgICAgICAgIFJCQyA9IFJCQyooMTBeNiksCiAgICAgICAgIGlkID0gcGFzdGUwKHN0dWR5LCBzdHJhaW4sIEJveCwgTW91c2UsIDIpKSAlPiUgCiAgc2VsZWN0KGRheSA9IERheSwKICAgICAgICAgbW91c2UgPSBNb3VzZSwKICAgICAgICAgUkJDLAogICAgICAgICBhc2V4ID0gUi5hc2V4LAogICAgICAgICBnYW0gPSBSLmdhbSwKICAgICAgICAgZG9zZSwKICAgICAgICAgc3RyYWluLAogICAgICAgICBzdHVkeSwKICAgICAgICAgc3R1ZHlfc3RyYWluLAogICAgICAgICBpZCkKCiMjIGZvciBldm9sdXRpb25fMjAxMCwgc2luZ2xlIGluZmVjdGlvbiBkYXRhIGZvciBib3RoIHJlc2lzdGFudCBhbmQgc3VzY2VwdGlibGUgY2xvbmVzIGFyZSBhdmFpbGFibGUgd2l0aG91dCBkcnVnIHRyZWF0bWVudApldm9sdXRpb25fMjAxMF9zcy5kZiA8LSBldm9fMjAxMCAlPiUgCiAgZmlsdGVyKENsb25lID09ICJSIiB8IENsb25lID09ICJTIikgJT4lCiAgZmlsdGVyKGJldHdlZW4oRGF5LCAzLCAyMSkgJgogICAgICAgICAgIERydWdzID09ICJub2RydWdzIikgJT4lIAogIG11dGF0ZShhc2V4ID0gUi5hc2V4ICsgUy5hc2V4LAogICAgICAgICBnYW0gPSBSLmdhbSArIFMuZ2FtLAogICAgICAgICBkb3NlID0gMTBeNiwKICAgICAgICAgUkJDID0gUkJDKigxMF42KSwKICAgICAgICAgc3R1ZHkgPSAiZXZvbDIwMTEiLAogICAgICAgICBzdHJhaW4gPSBpZmVsc2UoQ2xvbmUgPT0gIlIiLCAiQXMxMiIsICJBSjUxIiksCiAgICAgICAgIHN0dWR5X3N0cmFpbiA9IHBhc3RlMChzdHJhaW4sICJfIiwgc3R1ZHkpLAogICAgICAgICBpZCA9IHBhc3RlMChzdHVkeSwgc3RyYWluLCBCb3gsIE1vdXNlLCAzKSkgJT4lIAogIHNlbGVjdChkYXkgPSBEYXksCiAgICAgICAgIG1vdXNlID0gTW91c2UsCiAgICAgICAgIFJCQywKICAgICAgICAgYXNleCwKICAgICAgICAgZ2FtLAogICAgICAgICBkb3NlLAogICAgICAgICBzdHJhaW4sCiAgICAgICAgIHN0dWR5LAogICAgICAgICBzdHVkeV9zdHJhaW4sCiAgICAgICAgIGlkKQoKIyMgZm9yIGFtbmF0IDIwMTEsIGdldCBzaW5nbGUgaW5mZWN0aW9uIGRhdGEuIEZpbHRlciBvdXQgYW55IG1pY2UgdGhhdCBoYXZlIG1pc3NpbmcgZGF0YS4gU2V0IG5lZ2F0aXZlIGFzZXh1YXNsIGRhdGEgb3QgMAphbW5hXzIwMTFfc3MuZGYgPC0gIGFtbmFfMjAxMSAlPiUgCiAgZmlsdGVyKHRyZWF0ICVpbiUgYygiQUoiLCAiQVMiLCAiRVIiLCAiQ1IiLCAiQ1ciLCAiREsiKSkgJT4lIAogIG11dGF0ZShhc2V4ID0gdG90LnBhcmEgLSB0b3QuZ2N5dGUsCiAgICAgICAgIGdhbSA9IHRvdC5nY3l0ZSwKICAgICAgICAgZG9zZSA9IDEwXjYsCiAgICAgICAgIHN0dWR5ID0gImFtbmFfMjAxMSIsCiAgICAgICAgIFJCQyA9IHJiYy8oMTBeNiksCiAgICAgICAgIHN0dWR5X3N0cmFpbiA9IHBhc3RlMCh0cmVhdCwgIl8iLCBzdHVkeSksCiAgICAgICAgIGlkID0gcGFzdGUwKHN0dWR5LCB0cmVhdCwgZGl2LCBtb3VzZSwgNCkpICU+JSAKICBtdXRhdGUoYXNleCA9IGlmZWxzZShhc2V4IDwgMCwgMCwgYXNleCkpICMgc29tZXRpbWVzIHRvdGFsIHBhcmFzaXRlIGlzIGxlc3MgdGhhbiBnYW1ldG9jeXRlIHNvIG5lZWQgdG8gY29ycmVjdCBmb3IgdGhpcwoKIyMjIGNoZWNrIGZvciBOQSBieSBncm91cHMKYW1uYV9uYS5pZCA8LSBhbW5hXzIwMTFfc3MuZGYgJT4lIAogIGZpbHRlcl9hdCh2YXJzKGFzZXgsIGdhbSwgUkJDKSwgYWxsX3ZhcnMoaXMubmEoLikpKSAlPiUgCiAgZGlzdGluY3QoaWQpICU+JSAKICBzZWxlY3QoaWQpCgphbW5hXzIwMTFfc3MuZGYyIDwtIGFtbmFfMjAxMV9zcy5kZiAlPiUgCiAgZmlsdGVyKCEoaWQgJWluJSBhbW5hX25hLmlkJGlkKSkgJT4lIAogIHNlbGVjdChkYXksCiAgICAgICAgIG1vdXNlLAogICAgICAgICBSQkMsCiAgICAgICAgIGFzZXgsCiAgICAgICAgIGdhbSwKICAgICAgICAgZG9zZSwKICAgICAgICAgc3RyYWluID0gdHJlYXQsCiAgICAgICAgIHN0dWR5LAogICAgICAgICBzdHVkeV9zdHJhaW4sCiAgICAgICAgIGlkKQoKIyMgcmJpbmQKZXhwX3NzLmRmIDwtIHJiaW5kKGVtcGhfMjAxOF9zcy5kZiwgZXNlYl8yMDExX3NzLmRmLCBldm9sdXRpb25fMjAxMF9zcy5kZiwgYW1uYV8yMDExX3NzLmRmMikKCiMjIHdyaXRlCndyaXRlLmNzdihleHBfc3MuZGYsIGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL2V4cGVyaW1lbnRhbF9kYXRhLmNzdiIpKQpgYGAKCiMjIFByZXBhcmUgZGF0YXNldCBmb3IgcGxvdHRpbmcKYGBge3J9Cm5hbWVzKGV4cF9zcy5kZikgPC0gYygiWCIsICJEYXkiLCAiTW91c2UiLCAiUkJDIiwgImlSQkMiLCAiR2FtZXRvY3l0ZSIsICJEb3NlIiwgIlN0cmFpbiIsICJTdHVkeSIsICJTdHVkeV9zdHJhaW4iLCAiaWQiKQoKIyMgcHJlcGFyZSBhIGxpc3Qgb2YgdmFyaWFibGUgY29tYmluYXRpb25zIHdlIHdhbnQgdG8gcGxvdApleHBfdmFyLmNvbWIgPC0gdGlkeXI6OmV4cGFuZF9ncmlkKHggPSBjKCJSQkMiLCAiaVJCQyIsICJHYW1ldG9jeXRlIiksCiAgICAgICAgICAgICAgICAgICB5ID0gYygiUkJDIiwgImlSQkMiLCAiR2FtZXRvY3l0ZSIpKSAlPiUgIyMgZ2V0IGFsbCBwYWlyd2lzZSBjb21iaW5hdGlvbnMgb2YgdmFyaWFibGVzCiAgZmlsdGVyKHggIT0geSkgJT4lICMjIHJlbW92ZSBpbmNpZGVuY2VzIHdoZXJlIHRoZSAyIHZhcmlhYmxlcyBhcmUgdGhlIHNhbWUKICBtdXRhdGUodG1wID0gcGFzdGUwKHBtaW4oeCwgeSksIHBtYXgoeCwgeSkpKSAlPiUgIyMgZWxpbWluYXRlIHNhbWUgdmFyaWFibGUgYnV0IGRpZmZlcmVudCBvcmRlcgogIHNsaWNlX2hlYWQobiA9IDEsIGJ5ID0gdG1wKSAlPiUgCiAgc2VsZWN0KC10bXApIApgYGAKCiMjIExpc3Qtd2lzZSBwbG90dGluZwpgYGB7cn0KIyMgeCBhbmQgeSBheGlzIGFyZSBub3QgbG9nZ2VkIQpleHBfeHkucGxfbHMgPC0gbWFwMihleHBfdmFyLmNvbWIkeCwgZXhwX3Zhci5jb21iJHksIH4gewogIHhfY29sIDwtIC54CiAgeV9jb2wgPC0gLnkKICAKICBnZ3Bsb3QoZXhwX3NzLmRmLCBhZXNfc3RyaW5nKHggPSB4X2NvbCwgeSA9IHlfY29sKSkgKwogICAgICAgIGdlb21fcGF0aChhZXMoY29sb3VyID0gRGF5LCBncm91cCA9IGlkKSwgYXJyb3cgPSBhcnJvdyh0eXBlID0gImNsb3NlZCIsIGFuZ2xlID0gMTAsIGxlbmd0aCA9IHVuaXQoMCwgImluY2hlcyIpKSkgKwogICAgICAgIHRoZW1lX2NsYXNzaWMoKSArIAogICAgICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhvcHRpb24gPSAiQSIsIGxpbWl0cyA9IGMoMywgMjEpKSArCiAgICAgICAgbGFicyhjb2xvciA9ICJEYXlzIHBvc3QtaW5mZWN0aW9uIikgICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gZnVuY3Rpb24oeCkgZm9ybWF0KHgsIHNjaWVudGlmaWMgPSBUUlVFKSkgKwogICAgICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9zY2llbnRpZmljKGRpZ2l0cyA9IDEpKQp9CikKCiMjIHgtYXhpcyBpcyBsb2dnZWQKZXhwX3hsb2d5LnBsX2xzIDwtIG1hcDIoZXhwX3Zhci5jb21iJHgsIGV4cF92YXIuY29tYiR5LCB+IHsKICB4X2NvbCA8LSAueAogIHlfY29sIDwtIC55CiAgCiAgZ2dwbG90KGV4cF9zcy5kZiwgYWVzX3N0cmluZyh4ID0gc3ByaW50ZigibG9nMTAoJXMpIiwgeF9jb2wpLCB5ID0geV9jb2wpKSArCiAgICAgICAgZ2VvbV9wYXRoKGFlcyhjb2xvdXIgPSBEYXksIGdyb3VwID0gaWQpLCBhcnJvdyA9IGFycm93KHR5cGUgPSAiY2xvc2VkIiwgYW5nbGUgPSAxMCwgbGVuZ3RoID0gdW5pdCgwLCAiaW5jaGVzIikpKSArCiAgICAgICAgdGhlbWVfY2xhc3NpYygpICsgCiAgICAgICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJBIiwgbGltaXRzID0gYygzLCAyMSkpICsKICAgICAgICBsYWJzKGNvbG9yID0gIkRheXMgcG9zdC1pbmZlY3Rpb24iLCB4ID0gcGFzdGUoeF9jb2wsICJsb2ciKSkgICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gZnVuY3Rpb24oeCkgZm9ybWF0KHgsIHNjaWVudGlmaWMgPSBUUlVFKSkKfQopCgojIyB5LWF4aXMgbG9nZ2VkCmV4cF94eWxvZy5wbF9scyA8LSBtYXAyKGV4cF92YXIuY29tYiR4LCBleHBfdmFyLmNvbWIkeSwgfiB7CiAgeF9jb2wgPC0gLngKICB5X2NvbCA8LSAueQogIAogIGdncGxvdChleHBfc3MuZGYsIGFlc19zdHJpbmcoeCA9IHhfY29sLCB5ID0gc3ByaW50ZigibG9nMTAoJXMpIiwgeV9jb2wpKSkgKwogICAgICAgIGdlb21fcGF0aChhZXMoY29sb3VyID0gRGF5LCBncm91cCA9IGlkKSwgYXJyb3cgPSBhcnJvdyh0eXBlID0gImNsb3NlZCIsIGFuZ2xlID0gMTAsIGxlbmd0aCA9IHVuaXQoMCwgImluY2hlcyIpKSkgKwogICAgICAgIHRoZW1lX2NsYXNzaWMoKSArIAogICAgICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhvcHRpb24gPSAiQSIsIGxpbWl0cyA9IGMoMywgMjEpKSArCiAgICAgICAgbGFicyhjb2xvciA9ICJEYXlzIHBvc3QtaW5mZWN0aW9uIiwgeSA9IHBhc3RlKHlfY29sLCAibG9nIikpICAgKwogICAgICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9zY2llbnRpZmljKGRpZ2l0cyA9IDEpKQp9CikKCiMjIGJvdGggeCBhbmQgeSBheGlzIGlzIGxvZ2dlZApleHBfeGxvZ3lsb2cucGxfbHMgPC0gbWFwMihleHBfdmFyLmNvbWIkeCwgZXhwX3Zhci5jb21iJHksIH4gewogIHhfY29sIDwtIC54CiAgeV9jb2wgPC0gLnkKICAKICBnZ3Bsb3QoZXhwX3NzLmRmLCBhZXNfc3RyaW5nKHggPSBzcHJpbnRmKCJsb2cxMCglcykiLCB4X2NvbCksIHkgPSBzcHJpbnRmKCJsb2cxMCglcykiLCB5X2NvbCkpKSArCiAgICAgICAgZ2VvbV9wYXRoKGFlcyhjb2xvdXIgPSBEYXksIGdyb3VwID0gaWQpLCBhcnJvdyA9IGFycm93KHR5cGUgPSAiY2xvc2VkIiwgYW5nbGUgPSAxMCwgbGVuZ3RoID0gdW5pdCgwLCAiaW5jaGVzIikpKSArCiAgICAgICAgdGhlbWVfY2xhc3NpYygpICsgCiAgICAgICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJBIiwgbGltaXRzID0gYygzLCAyMSkpICsKICAgICAgICBsYWJzKGNvbG9yID0gIkRheXMgcG9zdC1pbmZlY3Rpb24iLCB4ID0gcGFzdGUoeF9jb2wsICJsb2ciKSwgeSA9IHBhc3RlKHlfY29sLCAibG9nIikpIAp9CikKCiMjIHBsb3QgdG9nZXRoZXIKZ2dhcnJhbmdlKHBsb3RsaXN0ID0gYyhleHBfeHkucGxfbHMsIGV4cF94bG9neS5wbF9scywgZXhwX3h5bG9nLnBsX2xzLCBleHBfeGxvZ3lsb2cucGxfbHMpLCAKICAgICAgICAgIGNvbW1vbi5sZWdlbmQgPSBULCBhbGlnbiA9ICJodiIpCmdnc2F2ZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZmlndXJlcy9leHBfZGlzZWFzZS1jdXJ2ZS50aWZmIiksIHVuaXRzID0gInB4Iiwgd2lkdGggPSAyMjUwLCBoZWlnaHQgPSAxNTAwLCBzY2FsZSA9IDEuNCwgZHBpPTMwMCwgIGJnID0gIndoaXRlIikKYGBgCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIFNpbXVsYXRlZCBkaXNlYXNlIGN1cnZlIGdyYXBoCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMjIEZ1bmN0aW9uIHRvIG9idGFpbiBkeW5hbWljcyBkYXRhIGJhc2VkIG9uIHRoZSBkdWFsIGN1ZSBpbnB1dApgYGB7cn0KZ2V0X2R1YWxfcm4gPC0gZnVuY3Rpb24oZGYpewogICMjIGFzc2lnbiB0aGUgdHdvIGN1ZXMKICBjdWUgPC0gdW5pcXVlKGRmJGN1ZSkKICBjdWVfYiA8LSB1bmlxdWUoZGYkY3VlX2IpCiAgCiAgIyMgYXNzaWduIGxvZyBzdGF0dXMKICBsb2cgPC0gaWZlbHNlKHN0cl9kZXRlY3QodW5pcXVlKGRmJGlkKSwgImxvZyIpLCAibG9nIiwgIm5vbmUiKQogIGxvZ19iIDwtIGlmZWxzZShzdHJfZGV0ZWN0KHVuaXF1ZShkZiRpZF9iKSwgImxvZyIpLCAibG9nIiwgIm5vbmUiKQogIAogICMjIGFzc2lnbiB3aGljaCBjdWVzIGFyZSBnb2luZyB0byBiZSBsb2dnZWQKICBpZihsb2cgPT0gImxvZyIgJiBsb2dfYiA9PSAibm9uZSIpe2xvZ2dlZF9jdWUgPC0gY3VlfQogIGlmKGxvZyA9PSAibm9uZSIgJiBsb2dfYiA9PSAibG9nIil7bG9nZ2VkX2N1ZSA8LSBjdWVfYn0KICBpZihsb2cgPT0gImxvZyIgJiBsb2dfYiA9PSAibG9nIil7bG9nZ2VkX2N1ZSA8LSBjKGN1ZSwgY3VlX2IpfQogIGlmKGxvZyA9PSAibm9uZSIgJiBsb2dfYiA9PSAibm9uZSIpe2xvZ2dlZF9jdWUgPC0gYygpfQoKICAjIyBrZWVwIHZhcmlhYmxlcyB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSBjdWUgdXNlZAogICMjIyBmb3IgZGF0YWZyYW1lcyB0aGF0IGRvZXMgbm90IGludm9sdmUgY29tYmluZWQgdmFyaWFibGVzIHN1Y2ggYXMgSStJZwogIGlmKGlzVFJVRShzdHJfZGV0ZWN0KGN1ZSwgIlxcKyIsIG5lZ2F0ZSA9IFQpKSAmIGlzVFJVRShzdHJfZGV0ZWN0KGN1ZV9iLCAiXFwrIiwgbmVnYXRlID0gVCkpKXsKICAgIGRmX2YgPC0gZGYgJT4lIAogICAgICBmaWx0ZXIodmFyaWFibGUgJWluJSBjKGN1ZSwgY3VlX2IsICJjciIpKSAlPiUgCiAgICAgIG11dGF0ZSh2YWx1ZSA9IGNhc2Vfd2hlbigKICAgICAgICB2YXJpYWJsZSAlaW4lIGxvZ2dlZF9jdWUgfiBsb2cxMCh2YWx1ZSksCiAgICAgICAgVFJVRSB+IHZhbHVlCiAgICAgICkpICU+JSAjIyBsb2cgdHJhbnNmb3JtIHZhbHVlcyBvbmx5IHdoZW4gdGhleSBtYXRjaCB3aXRoIHRoZSBsb2dnZWQgY3VlIGxpc3QKICAgICAgZmlsdGVyKHZhbHVlID49IDApICMjIGZpbHRlciBvdXQgdmFsdWVzIDwwLCB0aGVzZSBoYXBwZW4gZHVlIHRvIHN0aWZmbmVzcyBvZiBtb2RlbHMgYnV0IGFyZSBub3QgcmVsZXZhbnQKICAgICAgICAgCiAgfSBlbHNlewogICAgIyMjIGFzc2lnbiBib3RoIGN1ZXMgdG8gYSBsaXN0CiAgICBjdWVfbHMgPC0gYyhjdWUsIGN1ZV9iKQogICAgIyMjIHBpY2sgdGhlIGN1ZSB0aGF0IGhhcyB0aGUgIisiIHNpZ24KICAgIGNvbWJpbmVkX2N1ZSA8LSBjdWVfbHNbZ3JlcGwoIlxcKyIsIGN1ZV9scyldCiAgICBub25fY29tYmluZWRfY3VlIDwtIGN1ZV9sc1shZ3JlcGwoIlxcKyIsIGN1ZV9scyldICMjIyB0aGlzIGlzIHRoZSBub25lIGNvbWJpbmVkIGN1ZQogICAgIyMjIHVubGlzdCB0aGUgY3VlcwogICAgY3VlX3VubGlzdCA8LSB1bmxpc3Qoc3RyX3NwbGl0KGNvbWJpbmVkX2N1ZSwgIlxcKyIpKQogICAgCiAgICAjIyMgZ2V0IGZpbHRlcmVkIGRhdGFzZXQgY29udGFpbmluZyBvbmx5IG5vbi1jb21iaW5lIGN1ZQogICAgZGZfZjEgPC0gZGYgJT4lIAogICAgICBmaWx0ZXIodmFyaWFibGUgJWluJSBjKG5vbl9jb21iaW5lZF9jdWUsICJjciIpKQogICAgCiAgICAjIyMgZ2V0IGZpbHRlcmVkIGRhdGFzZXQgY29udGFpbmluZyBjb21iaW5lZCBjdWUuIFRoZXNlIHdpbGwgYmUgc3VtbWVkIHVwIGFuZCBib3VuZCBiYWNrIHRvIHRoZSBwcmV2aW91cwogICAgZGZfZjIgPC0gZGYgJT4lIAogICAgICBmaWx0ZXIodmFyaWFibGUgJWluJSBjdWVfdW5saXN0KSAlPiUgIyMga2VlcCBvbmx5IHZhcmlhYmxlcyB0aGF0IHdlIHdpbGwgY29tYmluZQogICAgICBncm91cF9ieSh0aW1lKSAlPiUgIyMgZm9yIGVhY2ggdGltZSBwb2ludCwgZ3JvdXAgdGhlIHZhcmlhYmxlcwogICAgICBtdXRhdGUodmFsdWUgPSBzdW0odmFsdWUsIG5hLnJtID0gVCksCiAgICAgICAgICAgICB2YXJpYWJsZSA9IGNvbWJpbmVkX2N1ZSkgJT4lICMjIHJlY2FsY3VsYXRlIHRoZSB2YWx1ZSBhcyBzdW0gb2YgdGhlIHZhbHVlcyBhbmQgcmVhc3NpZ24gdmFyaWFibGUhCiAgICAgIGRpc3RpbmN0KHRpbWUsIC5rZWVwX2FsbCA9IFQpICMjIG5vdGUgdGhhdCBiZWNhdXNlIHdlIGFyZSBtdXRhdGluZyB3ZSBtdXN0IGRlZGV1cGxpY2F0ZSB0aGUgcmVjb3JkcwogICAgCiAgICAjIyMjIGNvbWJpbmUgdGhlIHR3byBhbmQgbG9nIHRyYW5zZm9ybSBpZiBuZWNlc3NhcnkKICAgIGRmX2YgPC0gcmJpbmQoZGZfZjEsIGRmX2YyKSAlPiUgCiAgICAgIG11dGF0ZSh2YWx1ZSA9IGlmZWxzZSh2YXJpYWJsZSAlaW4lIGxvZ2dlZF9jdWUsIGxvZzEwKHZhbHVlKSwgdmFsdWUpKSAlPiUgIyMgbG9nIHRyYW5zZm9ybSB2YWx1ZXMgb25seSB3aGVuIHRoZXkgbWF0Y2ggd2l0aCB0aGUgbG9nZ2VkIGN1ZSBsaXN0CiAgICAgIGZpbHRlcih2YWx1ZSA+PSAwKSAKICB9CiAgCiAgIyMgQ29udmVydCBkYXRhZnJhbWVzIHdpZGVyIHN1Y2ggdGhhdCB0aGUgZGlmZmVyZW50IHZhcmlhYmxlcyBoYXZlIHRoZWlyIG93biBjb2x1bW5zCiAgZGZfZnAgPC0gZGZfZiAlPiUgCiAgICBtdXRhdGUodmFyaWFibGVfaWQgPSBpZmVsc2UodmFyaWFibGUgPT0gY3VlLCBwYXN0ZTAodmFyaWFibGUsICJfIiwgbG9nKSwgcGFzdGUwKHZhcmlhYmxlLCAiXyIsIGxvZ19iKSkpICU+JSAgIyMjIGFzc2lnbiBhIHVuaXF1ZSB2YXJpYWJsZSBpZCB0aGF0IGNvdWxkIGxhdGVyIGJlIHVzZWQgdG8gYXNzaWduIGxhYmVscwogICAgbGVmdF9qb2luKHNlbGVjdChlel9sYWJlbCwgaWQsIGxvbmdfbGFiZWwpLCBieSA9IGMoInZhcmlhYmxlX2lkIiA9ICJpZCIpKSAlPiUgCiAgICBtdXRhdGUobG9uZ19sYWJlbCA9IGlmZWxzZSh2YXJpYWJsZSA9PSAiY3IiLCAiY3IiLCBsb25nX2xhYmVsKSkgJT4lICMjIG1hbnVhbGx5IGFkZCBjcgogICAgbXV0YXRlKGxvbmdfbGFiZWwgPSBnc3ViKCIgIiwgIl8iLCBsb25nX2xhYmVsKSkgJT4lICAjIyBjb252ZXJ0IHNwYWNlcyB0byBfIGZvciBwbG90dGluZwogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGxvbmdfbGFiZWwsIHZhbHVlc19mcm9tID0gdmFsdWUsIGlkX2NvbHMgPSBjKHRpbWUsIGlkLCBpZF9iKSkgJT4lIAogICAgZmlsdGVyKHRpbWUgPj0gMSkgJT4lICMjIGZpbHRlciBvdXQgZGF5IDAtPjEgYmVjYXVzZSBhbGwgY3IgPSAwIGJlZm9yZSB0aGF0CiAgICBhcnJhbmdlKHRpbWUpICMjIHRoaXMgaXMgbmVlZGVkIHRvIHByZXZlbnQgZ2VvbV9wYXRoIGZyb20gam9pbmluZyB0aGUgZmlyc3QgYW5kIGxhc3QgZGF0YSBwb2ludAogIAogICMjIGFzc2lnbiBOQXMgKG1lYW5pbmcgbm8gc3R1ZmYgaXMgcHJvZHVjZWQgeWV0IHRvIDApCiAgZGZfZnBbaXMubmEoZGZfZnApXSA8LSAwCgogIHJldHVybihkZl9mcCkKfQpgYGAKCiMjIFJ1biBmdW5jdGlvbiB0byBnZXQgYSBjdXJhdGVkIGRhdGFzZXQgY29udGFpbmluZyBvbmx5IHJlbGV2YW50IApgYGB7cn0KIyMgc3BsaXQgZHVhbCBkeW5hbWljcyBkYXRhZnJhbWUgaW50byBsaXN0IGdyb3VwZWQgYnkgdGhlIGR1YWwgY3VlcwpkdWFsX2N1ZV9keW4ubHMgPC0gZHVhbF9jdWVfZHluLmRmICU+JSBncm91cF9zcGxpdChpZCwgaWRfYikKCiMjIHJ1biBmdW5jdGlvbiBhY3Jvc3MgbGlzdApkdWFsX2N1ZV9ybi5scyA8LSBtY2xhcHBseShkdWFsX2N1ZV9keW4ubHMsIGdldF9kdWFsX3JuLCBtYy5jb3JlcyA9IDYpCgojIyBzYW5pdHkgY2hlY2tzIHRoYXQgd2UgYXJlIGFjdHVhbGx5IHN1bW1pbmcgaVJCQ3MuIFJpbmdzIG91dAptYXgoZHVhbF9jdWVfcm4ubHNbWzJdXSRUb3RhbF9pUkJDKQptYXgoZHVhbF9jdWVfcm4ubHNbWzRdXSRBc2V4dWFsX2lSQkMpCm1heChkdWFsX2N1ZV9ybi5sc1tbNl1dJFNleHVhbF9pUkJDKQoKbWF4KGR1YWxfY3VlX3JuLmxzW1sxMF1dJFRvdGFsX2lSQkMpCm1heChkdWFsX2N1ZV9ybi5sc1tbMTJdXSRBc2V4dWFsX2lSQkMpCm1heChkdWFsX2N1ZV9ybi5sc1tbMTRdXSRTZXh1YWxfaVJCQykKYGBgCgojIyBwbG90CmBgYHtyfQojIyBsaXN0IGFwcGx5IGFsbCBkYXRhZnJhbWVzCmN1ZV9jdWVfcm5fcGwubHMgPC0gbGFwcGx5KGR1YWxfY3VlX3JuLmxzLAogICAgICAgZnVuY3Rpb24oeCl7CiAgICAgICAgICAjIyBnZXQgbmFtZXMgb2YgY29sdW1ucyB1c2VkIGluIHRoZSB4IGFuZCB5IGF4aXMKICAgICAgICAgYXhpc19jb2xzIDwtIHNldGRpZmYobmFtZXMoeCksIGMoInRpbWUiLCAiaWQiLCAiaWRfYiIsICJjciIpKQogICAgICAgICAKICAgICAgICAgIyMgZ2dwbG90CiAgICAgICAgIGdncGxvdCgpICsKICAgICAgICAgICBnZW9tX3BhdGgoZGF0YSA9IHgsIGFlc19zdHJpbmcoeCA9IGF4aXNfY29sc1tbMV1dLCB5ID0gYXhpc19jb2xzW1syXV0sIGNvbG9yID0gImNyIiksCiAgICAgICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdChjKHJlcCgwLCBucm93KHgpIC0gMiksIDAuMjUpLCAiaW5jaGVzIikpLAogICAgICAgICAgICAgICAgICAgICBzaXplID0gMS41KSArCiAgICAgICAgICAgZ2VvbV9wb2ludChkYXRhID0geCAlPiUgZmlsdGVyKHRpbWUgJSUgMSA9PSAwKSwgCiAgICAgICAgICAgICAgICAgICAgICBhZXNfc3RyaW5nKHggPSBheGlzX2NvbHNbWzFdXSwgeSA9IGF4aXNfY29sc1tbMl1dKSwgc2l6ZSA9IDEuNSwgc2hhcGUgPSAxKSArCiAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpICsKICAgICAgICAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2MobGltaXRzID0gYygwLCAxKSkgKwogICAgICAgICAgIGxhYnMoY29sb3IgPSAiQ29udmVyc2lvbiByYXRlIiwgeCA9IGdzdWIoIl8iLCAiICIsIGF4aXNfY29sc1tbMV1dKSwgeSA9IGdzdWIoIl8iLCAiICIsIGF4aXNfY29sc1tbMl1dKSkgKwogICAgICAgICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9zY2llbnRpZmljKGRpZ2l0cyA9IDIpKSArCiAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGxhYmVsX3NjaWVudGlmaWMoZGlnaXRzID0gMikpCiAgICAgICB9KQoKIyMgYXJyYW5nZSB0b2dldGhlcgpjdWVfY3VlX3JuLnBsIDwtIGdnYXJyYW5nZShwbG90bGlzdCA9IGN1ZV9jdWVfcm5fcGwubHMsIG5jb2wgPSA1LCBucm93ID0gOCwgY29tbW9uLmxlZ2VuZCA9IFQsIGFsaWduID0gImh2IikgCmdnc2F2ZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZmlndXJlcy9zaW1fZGlzZWFzZS1jdXJ2ZS50aWZmIiksIHVuaXRzID0gInB4Iiwgd2lkdGggPSAyMjUwLCBoZWlnaHQgPSAyNTAwLCBzY2FsZSA9IDIuMiwgZHBpPTMwMCwgIGJnID0gIndoaXRlIikKYGBgCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIEN1cmF0aW5nIGxpc3Qgb2Ygc2VsZWN0ZWQgZXhvIGFuZCBzaW11bGF0ZWQKIyBkaXNlYXNlIGN1cnZlcyAobWFpbiBmaWd1cmUpCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgcGxvdApgYGB7cn0KIyBtYWluIGZpZ3VyZQpnZ2FycmFuZ2UoCiAgIGN1ZV9jdWVfcm5fcGwubHNbWzMxXV0gKwogICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6bnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSkpICsKICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAxKSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgICAgICAgICAKICAgZXhwX3hsb2d5bG9nLnBsX2xzW1sxXV0gKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAoKICAgY3VlX2N1ZV9ybl9wbC5sc1tbMzVdXSArIAogICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAxKSkgKwogICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT02LjUpKSwgCiAgIAogICBleHBfeHlsb2cucGxfbHNbWzFdXSArIAogICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAxKSkgKwogICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT02LjUpKSwKICAgCiAgIGN1ZV9jdWVfcm5fcGwubHNbWzMwXV0gKyAKICAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xKSkgKwogICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIAogICAKICAgZXhwX3hsb2d5LnBsX2xzW1sxXV0gKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCAKICAgCiAgIGN1ZV9jdWVfcm5fcGwubHNbWzE1XV0gKyAKICAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xKSkgKwogICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIAogICAKICAgZXhwX3hsb2d5LnBsX2xzW1syXV0gKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogICAKICAgYWxpZ24gPSAiaHYiLCBuY29sID0gNCwgbnJvdyA9IDIKKQpnZ3NhdmUoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2ZpZ3VyZXMvc2ltX2V4cF9kaXNlYXNlX2N1cnZlX21haW4udGlmZiIpLCB1bml0cyA9ICJweCIsIHdpZHRoID0gMjI1MCwgaGVpZ2h0ID0gMTAwMCwgc2NhbGUgPSAxLjI1LCBkcGk9MzAwLCAgYmcgPSAid2hpdGUiKQoKCiMgZ2V0IGxlZ2VuZCBzZXBhcmF0ZWx5CmdnYXJyYW5nZSgKICAgY3VlX2N1ZV9ybl9wbC5sc1tbMzFdXSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xKSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6bnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIiksIAogICAgICAgICAgCiAgIGV4cF94bG9neWxvZy5wbF9sc1tbMV1dICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpKQpnZ3NhdmUoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2ZpZ3VyZXMvc2ltX2V4cF9kaXNlYXNlX2N1cnZlX2xlZ2VuZC50aWZmIiksIHVuaXRzID0gInB4Iiwgd2lkdGggPSAyMjUwLCBoZWlnaHQgPSA1MDAsIHNjYWxlID0gMS4yNSwgZHBpPTMwMCwgIGJnID0gIndoaXRlIikKCmBgYAoKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIE1DIHNpbXVsYXRpb24gb2Ygc2luZ2xlIGN1ZSBhbmQgZHVhbCBjdWUgaW5mZWN0aW9uCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMtLS0tLS0tLS0tLS0gSW1wYWN0IG9mIGFsbCBwYXJhbWV0ZXIgdmFyaWF0aW9uIG9uIGZpdG5lc3MgLS0tLS0tLS0tLS0tIwojIyBBcHBlbmQgYWxsIGZpdG5lc3MgZGF0YSBhbmQgc2FuaXR5IGNoZWNrcwpgYGB7cn0KIyMgSW1wb3J0IGFsbCBmaXRuZXNzIGRhdGEgYW5kIG1ha2UgaW50byBzaW5nbGUgZGF0YWZyYW1lCm1jX2FsbC5scyA8LSBsaXN0LmZpbGVzKHBhdGggPSBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19hbGxfZml0bmVzcyIpLCBwYXR0ZXJuID0gIiouY3N2IiwgZnVsbC5uYW1lcyA9IFQpCgojIyBmaWx0ZXIgb3V0IE1DIHJlY29yZHMgY29udGFpbmluZyBSIGxvZyAmIEkgbG9nLiBUaGVzZSBkYXRhIGhhdmUgZGlmZmVyZW50IGhlYWRlcnMgYW5kIHdvdWxkIG5lZWQgdG8gYmUgcHJvY2Vzc2VkIGRpZmZlcmVudGx5Cm1jX2FsbF9zYy5scyA8LSBtY19hbGwubHNbIWdyZXBsKCJSX2xvZzEwLUlfbG9nMTAqIiwgbWNfYWxsLmxzKV0KbWNfYWxsX1Jsb2dfSWxvZy5scyA8LSBtY19hbGwubHNbZ3JlcGwoIlJfbG9nMTAtSV9sb2cxMCoiLCBtY19hbGwubHMpXQoKIyMgd2UgYXJlIGV4cGVjdGluZyAxMSo1MDEwID0gNTUxMTAgZGF0YSBmaWxlcwpsZW5ndGgobWNfYWxsX3NjLmxzKQpsZW5ndGgobWNfYWxsX1Jsb2dfSWxvZy5scykgIyMgNTAxMCByZWNvcmRzIG5pY2UKCiMjIHJlYWQgYW5kIGFwcGVuZAptY19hbGxfZml0bmVzc19yYy5kZiA8LSBkby5jYWxsKHJiaW5kLCBtY2xhcHBseShtY19hbGxfc2MubHMsIGZ1bmN0aW9uKHgpe2RmIDwtIHJlYWQuY3N2KHgpfSwgbWMuY29yZXMgPSA2KSkKbWNfYWxsX2ZpdG5lc3NfUmxvZ19JbG9nLmRmIDwtIGRvLmNhbGwocmJpbmQsIG1jbGFwcGx5KG1jX2FsbF9SbG9nX0lsb2cubHMsIGZ1bmN0aW9uKHgpe2RmIDwtIHJlYWQuY3N2KHgpfSwgbWMuY29yZXMgPSA2KSkKCiMjIGp1c3QgY29ycmVjdCB0aGUgbG9nIC0+IGxvZzEwIGZvciBjb25zaXN0ZW5jeSBwdXJwb3NlIGFuZCBhbHNvIHJlbmFtZSBpZCB0byBpdGVyCm1jX2FsbF9maXRuZXNzLmRmIDwtIG1jX2FsbF9maXRuZXNzX1Jsb2dfSWxvZy5kZiAlPiUgCiAgbXV0YXRlKGN1ZSA9ICJSIGxvZyAmIEkgbG9nIiwgbG9nID0gImxvZzEwIikgJT4lICMjIHJlbmFtaW5nIHRoZSBjdWUgbmFtZXMgb2YgUiBsb2cgYW5kIEkgbG9nIGRhdGEgc28gdGhleSBmaXQKICBzZWxlY3QoLWMoY3VlX2IsIGxvZ19iKSkgJT4lIAogIHJiaW5kKG1jX2FsbF9maXRuZXNzX3JjLmRmKSAlPiUgCiAgbXV0YXRlKGxvZyA9IGlmZWxzZShsb2cgPT0gImxvZzEwIiwgImxvZyIsICJub25lIiksCiAgICAgICAgIGl0ZXIgPSBpZCkgJT4lIHNlbGVjdCgtaWQpCgojIyB3cml0ZQp3cml0ZV9wYXJxdWV0KG1jX2FsbF9maXRuZXNzLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19hbGxfZml0bmVzcy5wYXJxdWV0IikpCgojIyBmb3IgZWFjaCBjdWUgYW5kIGxvZywgaWQgPSAxIGlzIHdoZXJlIGFsbCBwYXJhbWV0ZXJzIGFyZSBkZWZhdWx0IHZhbHVlcy4gR2l2ZW4gdGhhdCB3ZSBzaW11bGF0ZWQgdGhlIHRpbWUgc3RlcCB3aXRoIDAuMDEgcmF0aGVyIHRoYW4gIDAuMDAxLCB3ZSBuZWVkIHRvIGNoZWNrIHdoZXRoZXIgdGhpcyBoaWdoZXIgdGltZSBzdGVwIGFsdGVycyB0aGUgZml0bmVzcyB2YWx1ZXMuIFdlIGNhbiBzZWUgdGhhdCB0aGUgZGlmZmVyZW5jZSBpcyB2ZXJ5IHNtYWxsIH4wLjAwMiBzbyB0aGlzIHdpbGwgbm90IGFmZmVjdCBhbnkgb3VyIG91dGNvbWVzLgptY19hbGxfZml0bmVzcy5kZiAlPiUgZmlsdGVyKGl0ZXIgPT0gMSkgJT4lIAogIGxlZnRfam9pbihzZWxlY3Qoc2lfb3B0LmRmLCBjdWUsIGxvZywgZml0bmVzc18yMCksIGJ5ID0gYygiY3VlIiwgImxvZyIpKSAlPiUgCiAgbXV0YXRlKGRpZmYgPSBtYXhfZml0bmVzcyAtIGZpdG5lc3NfMjApCmBgYAoKIyMgcHJvY2VzcyBkYXRhIGZvciBwbG90dGluZyBmaXRuZXNzCmBgYHtyfQojIyBhdHRhY2ggbGFiZWwKbWNfYWxsX2ZpdG5lc3MuZGZfcCA8LSBtY19hbGxfZml0bmVzcy5kZiAlPiUgCiAgbGVmdF9qb2luKGV6X2xhYmVsLCBieSA9IGMoImN1ZSIsICJsb2ciKSkgJT4lICMgZ2V0IGxhYmVsCiAgbXV0YXRlKGxvbmdfbGFiZWwgPSBpZmVsc2UoY3VlID09ICJSIGxvZyAmIEkgbG9nIiwgIlJCQyBsb2cgJlxuYXNleHVhbCBpUkJDIGxvZyIsIGxvbmdfbGFiZWwpLAogICAgICAgICBsb25nX2xhYmVsID0gaWZlbHNlKGN1ZSA9PSAidCIsICJUaW1lIiwgbG9uZ19sYWJlbCkpICMjIGFzc2lnbiBsYWJlbCBtYW51YWxseSB0byBkdWFsIGN1ZSBkYXRhCgojIyBtYW51YWxseSBjaGVjayB0aGF0IGV2ZXJ5dGhpbmcgaXMgYXNzaWduZWQgY29ycmVjdGx5Cm1jX2FsbF9maXRuZXNzLmRmX3AgJT4lIGRpc3RpbmN0KGxvbmdfbGFiZWwsIGN1ZSwgbG9nKQoKIyMgZ2V0IHRoZSByZWZlcmVuY2UgZml0bmVzcyAoZGVmYXVsdCBwYXJhbWV0ZXIgdmFyaWF0aW9uKSwgd2hpY2ggaXMgd2hlcmUgaXRlciA9IDEKbWNfYWxsX2ZpdG5lc3NfcmVmLmRmIDwtIG1jX2FsbF9maXRuZXNzLmRmX3AgJT4lIGZpbHRlcihpdGVyID09IDEpCgojIyBnZXQgdGhlIHJlc3Qgb2YgdGhlIGRhdGEgcG9pbnRzIChleGNsdWRpbmcgaXRlciA9PSAxKSBhbmQgY2FsY3VsYXRlIG1lZGlhbiBhbmQgbWVhbgptY19hbGxfZml0bmVzc19yYW5kLmRmIDwtIG1jX2FsbF9maXRuZXNzLmRmX3AgJT4lIAogIGZpbHRlcihpdGVyICE9IDEpIAoKIyMgZ2V0IG1lYW4gYW5kIG1vZGUgaW4gYSBzZXBhcmF0ZSBkZgptY19hbGxfZml0bmVzc19zdW0uZGYgPC0gbWNfYWxsX2ZpdG5lc3NfcmFuZC5kZiAlPiUgCiAgZ3JvdXBfYnkobG9uZ19sYWJlbCkgJT4lIAogIHN1bW1hcml6ZShtZWFuID0gbWVhbihtYXhfZml0bmVzcyksCiAgICAgICAgICAgIG1lZGlhbiA9IG1lZGlhbihtYXhfZml0bmVzcyksCiAgICAgICAgICAgIGdlb21fbWVhbiA9IGV4cChtZWFuKGxvZyhtYXhfZml0bmVzcykpKSkgCmBgYAoKIyMgcGxvdCBmaXRuZXNzIHZhcmlhdGlvbiAKYGBge3J9Cm1jX2FsbF9maXRuZXNzLnBsIDwtZ2dwbG90KCkgKwogIGdlb21fdmlvbGluKGRhdGEgPSBtY19hbGxfZml0bmVzc19yYW5kLmRmLCBhZXMoeCA9IG1heF9maXRuZXNzLCB5ID0gZmN0X3Jlb3JkZXIobG9uZ19sYWJlbCwgbWF4X2ZpdG5lc3MsIC5mdW4gPSBmdW5jdGlvbih4KXtleHAobWVhbihsb2coeCkpKX0pKSwKICAgICAgICAgICAgICBmaWxsID0gImxpZ2h0IGdyZXkiLCB0cmltID0gVCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IG1jX2FsbF9maXRuZXNzX3JlZi5kZiwgYWVzKHggPSBtYXhfZml0bmVzcywgeSA9IGxvbmdfbGFiZWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gIkRldGVybWluaXN0aWMiLCBjb2xvciA9ICJEZXRlcm1pbmlzdGljIiksIHNpemUgPSAzLCBhbHBoYSA9IDAuOCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IG1jX2FsbF9maXRuZXNzX3N1bS5kZiwgYWVzKHggPSBtZWFuLCB5ID0gbG9uZ19sYWJlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9ICJNZWFuIiwgY29sb3IgPSAiTWVhbiIpLCBzaXplID0gMywgYWxwaGEgPSAwLjgpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IG1jX2FsbF9maXRuZXNzX3N1bS5kZiwgYWVzKHggPSBnZW9tX21lYW4sIHkgPSBsb25nX2xhYmVsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gIkdlb21ldHJpYyBtZWFuIiwgY29sb3IgPSAiR2VvbWV0cmljIG1lYW4iKSwgc2l6ZSA9IDMsIGFscGhhID0gMC44KSArCiAgbGFicyh4ID0iRml0bmVzcyIsIHkgPSAiQ3VlcyIsIHNoYXBlID0gIiIsIGNvbG9yID0gIiIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCBibHVlLCAiI0Y4NzY2RCIpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBleHBhbmRfbGltaXRzKHkgPSAxMy43KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjIsMSksIAogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsCiAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAidG9wIikgCmBgYAoKIy0tLS0tLS0tLS0tLSBJbXBhY3Qgb2YgaW5kaXZpZHVhbCBwYXJhbWV0ZXIgdmFyaWF0aW9uIG9uIGZpdG5lc3MgLS0tLS0tLS0tLS0tIwojIyBjb21iaW5lIGFsbCBzaW5nbGUgcGFyYW1ldGVyIHZhcmlhdGlvbiBmaWxlcwpgYGB7cn0KIyMgbGlzdCBvZiBmaWxlIHBhdGhzIGxpbmtlZCB0byB0aGUgc2luZ2xlIHBhcmFtZXRlciBmaWxlcwptY19zaW5nbGUubHMgPC0gbGlzdC5maWxlcyhwYXRoID0gaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvbWNfc2luZ2xlX2ZpdG5lc3MiKSwgcGF0dGVybiA9ICIqLmNzdiIsIGZ1bGwubmFtZXMgPSBUKQoKIyMgZmlsdGVyIG91dCBSIGxvZyBhbmQgSSBsb2cgZGF0YS4gdGhlc2Ugd2lsbCBiZSBhdHRhY2hlZCBsYXRlcgptY19zaW5nbGVfc2MubHMgPC0gbWNfc2luZ2xlLmxzWyFncmVwbCgiUl9sb2cxMC1JX2xvZzEwKiIsIG1jX3NpbmdsZS5scyldCm1jX3NpbmdsZV9SbG9nX0lsb2cubHMgPC0gbWNfc2luZ2xlLmxzW2dyZXBsKCJSX2xvZzEwLUlfbG9nMTAqIiwgbWNfc2luZ2xlLmxzKV0KCiMjIGNoZWNrIG51bWJlciBvZiBmaWxlcwpsZW5ndGgobWNfc2luZ2xlX3NjLmxzKQpsZW5ndGgobWNfc2luZ2xlX1Jsb2dfSWxvZy5scykKCiMjIHJlYWQgYW5kIGNvbWJpbmUKbWNfc2luZ2xlX3NjX2ZpdG5lc3MuZGYgPC0gZG8uY2FsbChyYmluZCwgbWNsYXBwbHkobWNfc2luZ2xlX3NjLmxzLCBmdW5jdGlvbih4KSByZWFkLmNzdih4KSwgbWMuY29yZXMgPSA2KSkKbWNfc2luZ2xlX1Jsb2dfSWxvZ19maXRuZXNzLmRmIDwtIGRvLmNhbGwocmJpbmQsIG1jbGFwcGx5KG1jX3NpbmdsZV9SbG9nX0lsb2cubHMsIGZ1bmN0aW9uKHgpIHJlYWQuY3N2KHgpLCBtYy5jb3JlcyA9IDYpKQoKIyMgY2hhbmdlIGlkIC0+IGl0ZXIgYW5kIGNvcnJlY3RpbmcgbG9nIGxhYmVsCm1jX3NpbmdsZV9maXRuZXNzLmRmIDwtIG1jX3NpbmdsZV9SbG9nX0lsb2dfZml0bmVzcy5kZiAlPiUgCiAgbXV0YXRlKGN1ZSA9ICJSIGxvZyAmIEkgbG9nIikgJT4lICMjIG1hbnVhbGx5IHJlY29kZSBjdWUgZm9yIHRoZSBkdWFsIGN1ZSByZXN1bHRzCiAgc2VsZWN0KC1jKGN1ZV9iLCBsb2dfYikpICU+JSAjIyB0aGVzZSBjb2x1bW5zIGFyZSBub3QgcHJlc2VudCBpbiB0aGUgc2luZ2xlIGN1ZSBtb2RlbHMgYW5kIGFyZSByZW1vdmVkCiAgcmJpbmQobWNfc2luZ2xlX3NjX2ZpdG5lc3MuZGYpICU+JSAKICBtdXRhdGUobG9nID0gaWZlbHNlKGxvZyA9PSAibG9nMTAiLCAibG9nIiwgIm5vbmUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpdGVyID0gaWQpICU+JSBzZWxlY3QoLWlkKQojIyB3cml0ZQp3cml0ZV9wYXJxdWV0KG1jX3NpbmdsZV9maXRuZXNzLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19zaW5nbGVfZml0bmVzcy5wYXJxdWV0IikpCgojIyBzYW5pdHkgY2hlY2suIGl0ZXIgPSAxIGlzIHdoZXJlIGFsbCBwYXJhbWV0ZXJzIGFyZSBkZWZhdWx0LiBTaG91bGQgcHJvZHVjZSB0aGUgc2FtZSBmaXRuZXNzIHZhbHVlcyEKbWNfc2luZ2xlX2ZpdG5lc3MuZGYgJT4lIGZpbHRlcihpdGVyID09IDEpCgojIyBzYW5pdHkgY2hlY2suIFdlIHNob3VsZCBoYXZlIHRoZSBzYW1lIHZhbHVlcyBmb3IgaXRlciAxIGFjcm9zcyBhbGwgYW5kIHNpbmdsZSBwYXJhbWV0ZXIgZGF0YXNldHMuIAptY19zaW5nbGVfZml0bmVzcy5kZiAlPiUgZmlsdGVyKGl0ZXIgPT0gMSkKYGBgCgojIyBwcm9jZXNzIGRhdGEgZm9yIHBsb3R0aW5nCmBgYHtyfQojIyBub3RlIHRoYXQgZm9yIGVhY2ggaXRlciBhY3Jvc3MgdGhlICJzaW5nbGUiIGFuZCAiYWxsIiBkYXRhc2V0LCB0aGUgcGFyYW1ldGVyIGFsdGVyYXRpb24gaXMgdGhlIHNhbWUuIFRodXMsIGZvciBlYWNoIGRhdGEgcG9pbnQgYXQgd2hpY2ggYWxsIHBhcmFtZXRlciBhcmUgdmFyaWVkLCB0aGVyZSBpcyBhIGNvcnJlc3BvbmRpbmcgZGF0YXBvaW50IHdoZXJlIG9ubHkgb25lIHBhcmFtZXRlciBpcyB2YXJpZWQuIFdlIGNhbiBqb2luIHRoZXNlIDIgZGF0YXNldCBieSBpdGVyIGFuZCBjdWUgYW5kIGxvZwptY19zaW5nbGVfYWxsX2ZpdG5lc3MuZGYgPC0gbWNfc2luZ2xlX2ZpdG5lc3MuZGYgJT4lIAogIGxlZnRfam9pbihzZWxlY3QobWNfYWxsX2ZpdG5lc3MuZGYsIGZpdG5lc3NfYWxsID0gbWF4X2ZpdG5lc3MsIGN1ZSwgbG9nLCBpdGVyKSwgYnkgPSBjKCJjdWUiLCAibG9nIiwgIml0ZXIiKSkKCiMjIG1ha2UgdGhlIGRhdGFmcmFtZSBpbnRvIGEgbG9uZyBmb3JtYXQgc3VjaCB0aGF0IGFsbCBmaXRuZXNzIHZhcmlhdGlvbnMgYXJlIGluIGEgc2luZ2xlIGNvbHVtbgptY19zaW5nbGVfZml0bmVzcy5sb25nIDwtIG1jX3NpbmdsZV9hbGxfZml0bmVzcy5kZiAlPiUgCiAgZmlsdGVyKGl0ZXIgIT0gMSkgJT4lICMjIGZpbHRlciBvdXQgaXRlciA9IDEsIHdoaWNoIGRvZXMgbm90IGhhdmUgdmFyaWF0aW9uCiAgbGVmdF9qb2luKHNlbGVjdChtY19hbGxfZml0bmVzc19yZWYuZGYsIGZpdG5lc3NfcmVmID0gbWF4X2ZpdG5lc3MsIGN1ZSAsIGxvZyksCiAgICAgICAgICAgIGJ5ID0gYygiY3VlIiwgImxvZyIpKSAlPiUgIyMgYWRkIGluIHRoZSBkZXRlcm1pbmlzdGljIGZpdG5lc3MgdmFsdWVzCiAgc2VsZWN0KC1jKCJYIiwgInJobyIsICJidXJzdCIsICJpb3RhX04xIiwgImlvdGFfTjIiLCAicGhpX04xIiwgInBoaV9OMiIpKSAlPiUgIyBrZWVwIG9ubHkgZml0bmVzcyBhbmQgYXNzb2NpYXRlZCBsYWJlbHMKICB0aWR5cjo6cGl2b3RfbG9uZ2VyKC1jKCJjdWUiLCAibG9nIiwgImZpdG5lc3NfYWxsIiwgIml0ZXIiLCAiZml0bmVzc19yZWYiKSkgJT4lICMjIG1ha2UgbG9uZwogIG11dGF0ZShwYXJhbWV0ZXIgPSBnc3ViKCJmaXRuZXNzXyIsICIiLCBuYW1lKSkgIyMgaXNvbGF0ZSBwYXJhbWV0ZXIgYmVpbmcgYWx0ZXJlZAoKIyMgY2FsY3VsYXRlIGRlZ3JlZSBkZXZpYXRpb24gZnJvbSBkZXRlcm1pbmlzdGljIHZhbHVlcy4gbm90ZSB0aGF0IHJlbF9kaWZmX3NpbmdsZSByYW5nZXMgZnJvbSAtMSB0byAxLiAtMSAtPiAgb25lIHZhcmlhYmxlIHBlcnR1cmJhdGlvbiBpcyBhY3RpbmcgaW4gdGhlIG9wcG9zaXRlIGRpcmVjdGlvbiB0byB0aGUgb3ZlcmFsbCBwZXJ0dXJiYXRpb24gY2F1c2VkIGJ5IHJhbmRvbWl6aW5nIGFsbCB2YXJpYWJsZXMuIDAgcGFyYW1ldGVyIHZhcmlhdGlvbiBjb250cmlidXRlcyB2ZXJ5IGxpdHRsZSwgMSAtPiBvbmUgdmFyaWFibGUgY29udHJpYnV0ZXMgYSBsb3QKbWNfc2luZ2xlX2ZpdG5lc3MubG9uZ19wIDwtIG1jX3NpbmdsZV9maXRuZXNzLmxvbmcgJT4lIAogIG11dGF0ZShkaWZmX3NpbmdsZSA9IHZhbHVlLWZpdG5lc3NfcmVmLCAjIyBwZXJ0dWJhdGlvbiB0byBmaXRuZXNzIGNhdXNlZCBieSBzaW5nbGUgcGFyYW1ldGVyIHZhcmlhdGlvbgogICAgICAgICBkaWZmX2FsbCA9IGZpdG5lc3NfYWxsLWZpdG5lc3NfcmVmLCAjIyBwZXJ0dWJhdGlvbiB0byBmaW50ZXNzIGNhdXNlZCBieSBhbGwgcGFyYW1ldGVyIHZhcnlpbmcKICAgICAgICAgcmVsX2RpZmZfc2luZ2xlID0gZGlmZl9zaW5nbGUvZGlmZl9hbGwgIyMgbm9ybWFsaXplZCBwZXJ0dW5hdGlvbiB0byBmaXRuZXNzIChzaW5nbGUgcGFyYW1ldGVyKQogICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oZXpfbGFiZWwsIGJ5ID0gYygiY3VlIiwgImxvZyIpKSAlPiUgIyBnZXQgbGFiZWwKICBtdXRhdGUobG9uZ19sYWJlbCA9IGlmZWxzZShjdWUgPT0gIlIgbG9nICYgSSBsb2ciLCAiUkJDIGxvZyAmXG5hc2V4dWFsIGlSQkMgbG9nIiwgbG9uZ19sYWJlbCkpICMjIG1hdWFsbHkgYXNzaWduIGxhYmVsIHRvIGR1YWwgY3VlIGRhdGEKCiMjIGNhbGN1bGF0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MuIHRoaXMgaW5jbHVkZXMgbWVkaWFuLCBjcmVkaWJsZSBpbnRlcnZhbCAoY29udGFpbnMgODklIGRhdGEgcG9pbnRzIGNhbGN1bGF0ZWQgdmlhIEhpZ2hlc3QgRGVuc2l0eSBJbnRlcnZhbCwgd2hpY2ggaXMgYmV0dGVyIGZvciBza2V3ZWQgZGF0YSkKbWNfc2luZ2xlX2ZpdG5lc3Muc3VtIDwtIG1jX3NpbmdsZV9maXRuZXNzLmxvbmdfcCAlPiUgCiAgZ3JvdXBfYnkobG9uZ19sYWJlbCwgcGFyYW1ldGVyKSAlPiUgCiAgc3VtbWFyaXNlKGNpX2xvd2VyID0gY2kocmVsX2RpZmZfc2luZ2xlLCBtZXRob2QgPSAiSERJIiwgY2kgPSAwLjg5KVtbMl1dLAogICAgICAgICAgICBjaV9oaWdoZXIgPSBjaShyZWxfZGlmZl9zaW5nbGUsIG1ldGhvZCA9ICJIREkiLCBjaSA9IDAuODkpW1szXV0sCiAgICAgICAgICAgIHF1YW50aWxlX2xvdyA9IHF1YW50aWxlKHJlbF9kaWZmX3NpbmdsZSwgMC4wMjUpLAogICAgICAgICAgICBxdWFudGlsZV9oaWdoID0gcXVhbnRpbGUocmVsX2RpZmZfc2luZ2xlLCAwLjk3NSksCiAgICAgICAgICAgIG1lZGlhbiA9IG1lZGlhbihyZWxfZGlmZl9zaW5nbGUpLAogICAgICAgICAgICBtZWFuID0gbWVhbihyZWxfZGlmZl9zaW5nbGUpKSAlPiUgCiAgbXV0YXRlKHBhcmFtZXRlcl9sYWJlbCA9IGNhc2Vfd2hlbiggIyMgcmVjb2RlIHBhcmFtZXRlciB2YWx1ZXMKICAgIHBhcmFtZXRlciA9PSAicmhvIiB+ICJSQkMgcmVwbGVuaXNobWVudCAoz4EpIiwKICAgIHBhcmFtZXRlciA9PSAicGhpbiIgfiAiSGFsZi1saWZlIGluZGlzICjPlW4pIiwKICAgIHBhcmFtZXRlciA9PSAicGhpdyIgfiAiSGFsZi1saWZlIHRhcmdldGVkICjPlXcpIiwKICAgIHBhcmFtZXRlciA9PSAicHNpbiIgfiAiQWN0aXZhdGlvbiBpbmRpcyAoz4huKSIsCiAgICBwYXJhbWV0ZXIgPT0gInBzaXciIH4gIkFjdGl2YXRpb24gdGFyZ2V0ZWQgKM+IdykiLAogICAgcGFyYW1ldGVyID09ICJiZXRhIiB+ICJCdXJzdCBzaXplICjOsikiLAogICkpCgoKIyMgZm9yIHZpb2xpbiBwbG90cywgd2hhdCB3ZSBjYW4gZG8gaXMgcGxvdCBvdXQgb25seSB0aGUgODklIGNyZWRpYmxlIGludGVydmFsIHNvIHRoZSBncmFwaCBpcyBlYXNpZXIgdG8gaW50ZXJwcmV0Cm1jX3NpbmdsZV9maXRuZXNzLmxvbmdfcF9mIDwtIG1jX3NpbmdsZV9maXRuZXNzLmxvbmdfcCAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChtY19zaW5nbGVfZml0bmVzcy5zdW0sIGxvbmdfbGFiZWwsIHBhcmFtZXRlciwgY2lfbG93ZXIsIGNpX2hpZ2hlciwgcGFyYW1ldGVyX2xhYmVsKSwgYnkgPSBjKCJsb25nX2xhYmVsIiwgInBhcmFtZXRlciIpKSAlPiUgCiAgZmlsdGVyKHJlbF9kaWZmX3NpbmdsZSA+PSBjaV9sb3dlciAmIHJlbF9kaWZmX3NpbmdsZSA8IGNpX2hpZ2hlcikKCgojIyBhcnJhbmdlIG9yZGVyaW5nIG9mIGN1ZXMgYW5kIHBhcmFtZXRlcnMKbWNfc2luZ2xlX2ZpdG5lc3MubG9uZ19wX2YkbG9uZ19sYWJlbCA8LSBmYWN0b3IobWNfc2luZ2xlX2ZpdG5lc3MubG9uZ19wX2YkbG9uZ19sYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIlJCQyBsb2cgJlxuYXNleHVhbCBpUkJDIGxvZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBc2V4dWFsIGlSQkMiLCAiQXNleHVhbCBpUkJDIGxvZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTZXh1YWwgaVJCQyIsICJTZXh1YWwgaVJCQyBsb2ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90YWwgaVJCQyIsICJUb3RhbCBpUkJDIGxvZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHYW1ldG9jeXRlIiwgIkdhbWV0b2N5dGUgbG9nIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSQkMiLCAiUkJDIGxvZyIpKQoKbWNfc2luZ2xlX2ZpdG5lc3MubG9uZ19wX2YkcGFyYW1ldGVyX2xhYmVsIDwtIGZhY3RvcihtY19zaW5nbGVfZml0bmVzcy5sb25nX3BfZiRwYXJhbWV0ZXJfbGFiZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiQnVyc3Qgc2l6ZSAozrIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSQkMgcmVwbGVuaXNobWVudCAoz4EpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIYWxmLWxpZmUgaW5kaXMgKM+VbikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhhbGYtbGlmZSB0YXJnZXRlZCAoz5V3KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQWN0aXZhdGlvbiBpbmRpcyAoz4huKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQWN0aXZhdGlvbiB0YXJnZXRlZCAoz4h3KSIpKQpgYGAKCiMgcGxvdHRpbmcgODklIGNyZWRpYmxlIGludGVydmFsLiBCeSB2aXN1YWwgaW5zcGVjdGlvbiBvZiB0aGUgZGlzdHJpYnV0aW9uLCBjaSByZXByZXNlbnRlZCB0aGUgZGlzdHJpYnV0aW9uIChtb3JlIGhvbmVzdGx5KSB0aGFuIHF1YW50aWxlLCBldmVuIHdoZW4gcXVhbnRpbGUgc2VlbXMgdG8gZGlzcGxheSBsYXJnZXIgZGlmZmVyZW5jZXMgb2YgbG9nZ2luZyBjdWVzLgpgYGB7cn0KbWNfcGFydGl0aW9uLnBsIDwtIGdncGxvdCgpICsKICBnZW9tX3Zpb2xpbihkYXRhID0gbWNfc2luZ2xlX2ZpdG5lc3MubG9uZ19wX2YsIGFlcyh4ID0gcmVsX2RpZmZfc2luZ2xlLCB5ID0gbG9uZ19sYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBsb2csIGNvbG9yID0gbG9nKSkgKyAjIyA4OSUgY3JlZGlibGUgaW50ZXJ2YWwKICBnZW9tX3BvaW50KGRhdGEgPSBtY19zaW5nbGVfZml0bmVzcy5zdW0sIGFlcyh4ID0gbWVkaWFuLCB5ID0gbG9uZ19sYWJlbCkpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZmFjZXRfd3JhcCh+cGFyYW1ldGVyX2xhYmVsKSArCiAgbGFicyh4ID0gIlJlbGF0aXZlIGZpdG5lc3MgcGVydHViYXRpb24iLCB5ID0gIkN1ZXMiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjNDU3NWI0IiwgIiNmYzhkNTkiKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjNDU3NWI0IiwgIiNmYzhkNTkiKSkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDIyLDIzKSkgKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX3lfZGlzY3JldGUobGltaXRzPXJldikgKyAjIyByZXZlcnNlIG9yZGVyaW5nIG9mIHktYXhpcyBzbyB0aGF0IGN1ZXMgYXJlIGRpc3BsYXllZCBjb3JyZWN0bHkKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpCiAgKQpgYGAKCiMtLS0tLS0tLS0gYXJyYW5nZSBwbG90cyB0b2dldGhlciAtLS0tLS0tLS0jCmBgYHtyfQpnZ2FycmFuZ2UobWNfYWxsX2ZpdG5lc3MucGwsIG1jX3BhcnRpdGlvbi5wbCwgYWxpZ24gPSAiaCIsIHdpZHRocyA9IGMoMS4zLCAyKSwgbGFiZWxzID0gYygiQSIsICJCIikpCmdnc2F2ZSh1bml0cyA9ICJweCIsIGRwaSA9IDMwMCwgd2lkdGggPSAyMjUwLCBoZWlnaHQgPSAxNTAwLCBmaWxlbmFtZSA9IGhlcmUoImNvZGVfcmVwb3NpdG9yeS9maWd1cmVzL21jX2ZpdG5lc3NfcGFydGl0aW9uLnRpZmYiKSwgYmcgPSAid2hpdGUiLCBzY2FsZSA9IDEuMikKYGBgCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIFN1cHBsZW1lbnRhcnkgZmlndXJlIG9uIE1DIHBvc3RlcmlvciBwYXJhbWV0ZXIgZGlzdHJpYnV0aW9uCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMjIFByb2Nlc3MgZGF0YQpgYGB7cn0KIyMgbWFudWFsbHkgbWFrZSBkYXRhZnJhbWUgY29udGFpbmluZyB0aGUgcGFyYW1ldGVyIHZhbHVlcyB1c2VkIGluIHRoZSBkZXRlcm1pbmlzdGljIG1vZGVsCnBhcl9kZXQuZGYgPC0gZGF0YS5mcmFtZSgKICBwYXJhbWV0ZXIgPSBjKCJyaG8iLCAicGhpX04xIiwgInBoaV9OMiIsICJpb3RhX04xIiwgImlvdGFfTjIiLCAiYnVyc3QiKSwKICBkZXRlcm1pbmlzdGljID0gYygyLjYyNzE1NmUtMDEsIDMuNTIwNTkxZS0wMiwgNS41MDg0MjBlKzAyLCAxLjY2OTIzNGUrMDEsIDguNDMxNzg1ZS0wMSwgNS43MjEwMDBlKzAwICkKKQoKIyMgbWFrZSBpbnRvIGxvbmcgZm9ybWF0CnBvc3Rlcmlvci5sb25nIDwtIHBvc3Rlcmlvci5kZiAlPiUgCiAgdGlkeXI6OnBpdm90X2xvbmdlcigtaWQsIG5hbWVzX3RvID0gInBhcmFtZXRlciIpICU+JSAKICBsZWZ0X2pvaW4ocGFyX2RldC5kZiwgYnkgPSAicGFyYW1ldGVyIikgJT4lICMjIGFkZCBpbiB0aGUgZGV0ZXJtaW5pc3RpYyBwYXJhbWV0ZXIgdmFsdWVzCiAgbXV0YXRlKGxhYmVsID0gY2FzZV93aGVuKAogICAgcGFyYW1ldGVyID09ICJyaG8iIH4gIlJCQyByZXBsZW5pc2htZW50ICjPgSkiLAogICAgcGFyYW1ldGVyID09ICJwaGlfTjEiIH4gIkhhbGYtbGlmZSBpbmRpcyAoz5VuKSIsCiAgICBwYXJhbWV0ZXIgPT0gInBoaV9OMiIgfiAiSGFsZi1saWZlIHRhcmdldGVkICjPlXcpIiwKICAgIHBhcmFtZXRlciA9PSAiaW90YV9OMSIgfiAiQWN0aXZhdGlvbiBpbmRpcyAoz4huKSIsCiAgICBwYXJhbWV0ZXIgPT0gImlvdGFfTjIiIH4gIkFjdGl2YXRpb24gdGFyZ2V0ZWQgKM+IdykiLAogICAgcGFyYW1ldGVyID09ICJidXJzdCIgfiAiQnVyc3Qgc2l6ZSAozrIpIgogICkpICMjIHJlbmFtZSBwYXJhbXRlciB2YWx1ZXMKCmBgYAoKIyMgcGxvdApgYGB7cn0KIyMgcG9ydGlvbiBvZiBwYXJhbWV0ZXIgdmFsdWVzIHRoYXQgZG9lcyBub3QgbmVlZCBsb2ctdHJhbnNmb3JtaW5nCnBvc3Rlcmlvcl8xLnBsIDwtIGdncGxvdChwb3N0ZXJpb3IubG9uZyAlPiUgZmlsdGVyKCFwYXJhbWV0ZXIgJWluJSBjKCJwaGlfTjEiLCAicGhpX04yIiwgImlvdGFfTjEiKSkpICsKICBnZW9tX2RlbnNpdHkoYWVzKHggPSB2YWx1ZSksIGZpbGwgPSAiZ3JleSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gZGV0ZXJtaW5pc3RpYyksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBmYWNldF93cmFwKH5sYWJlbCwgc2NhbGVzID0gImZyZWUiKSArCiAgbGFicyh4ID0gIiIsIHkgPSAiRGVuc2l0eSIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCgojIyBQbG90cyB3aGVyZSB0aGUgeC1heGlzIHNob3VsZCBiZSBsb2ctdHJhbnNmb3JtZWQgc28gaXQgaXMgZWFzaWVyIHRvIHJlYWQKcG9zdGVyaW9yXzIucGwgPC0gZ2dwbG90KHBvc3Rlcmlvci5sb25nICU+JSBmaWx0ZXIocGFyYW1ldGVyICVpbiUgYygicGhpX04xIiwgInBoaV9OMiIsICJpb3RhX04xIikpKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gdmFsdWUpLCBmaWxsID0gImdyZXkiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IGRldGVybWluaXN0aWMpLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZmFjZXRfd3JhcCh+bGFiZWwsIHNjYWxlcyA9ICJmcmVlIikgKwogIGxhYnMoeCA9ICJWYWx1ZSIsIHkgPSAiRGVuc2l0eSIpICsKICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMTAiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQoKIyMgcGxvdCB0b2dldGhlcgpnZ2FycmFuZ2UocG9zdGVyaW9yXzEucGwsIHBvc3Rlcmlvcl8yLnBsLCBuY29sID0gMSwgYWxpZ24gPSAiaHYiKQogIAojIyBzYXZlCmdnc2F2ZSh1bml0cyA9ICJweCIsIGRwaSA9IDMwMCwgd2lkdGggPSAyMDAwLCBoZWlnaHQgPSAxMzAwLCBmaWxlbmFtZSA9IGhlcmUoImNvZGVfcmVwb3NpdG9yeS9maWd1cmVzL3Bvc3Rlcmlvci50aWZmIiksIGJnID0gIndoaXRlIiwgc2NhbGUgPSAxKQpgYGAKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgUmFuayBwbG90IG9mIHBhcmFzaXRlIGZpdG5lc3Mgd2l0aCBvbmx5IG9uZSBwYXJhbWV0ZXIgdmFyeWluZwojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIyBQcm9jZXNzIGRhdGEKYGBge3J9CiMjIHNpbXBsZXIgdmVyc2lvbiBvZiBsb25nIHRhYmxlIGNvbnRhaW5pbmcgZml0bmVzcyBvZiBwYXJhc2l0ZXMgd2hlbiB2YXJpb3VzIHBhcmFtZXRlciBpcyBwZXJ0dWJlZAptY19zaW5nbGVfZml0bmVzcy5sb25nMiA8LSBtY19zaW5nbGVfZml0bmVzcy5kZiAlPiUgCiAgZmlsdGVyKGl0ZXIgIT0gMSkgJT4lICMjIGZpbHRlciBvdXQgaXRlciA9IDEsIHdoaWNoIGRvZXMgbm90IGhhdmUgdmFyaWF0aW9uCiAgc2VsZWN0KC1jKCJYIiwgInJobyIsICJidXJzdCIsICJpb3RhX04xIiwgImlvdGFfTjIiLCAicGhpX04xIiwgInBoaV9OMiIpKSAlPiUgIyBrZWVwIG9ubHkgZml0bmVzcyBhbmQgYXNzb2NpYXRlZCBsYWJlbHMKICB0aWR5cjo6cGl2b3RfbG9uZ2VyKC1jKCJjdWUiLCAibG9nIiwgIml0ZXIiKSkgJT4lICMjIG1ha2UgbG9uZwogIG11dGF0ZShwYXJhbWV0ZXIgPSBnc3ViKCJmaXRuZXNzXyIsICIiLCBuYW1lKSkgJT4lIAogIGxlZnRfam9pbihlel9sYWJlbCwgYnkgPSBjKCJjdWUiLCAibG9nIikpICU+JSAKICBtdXRhdGUobG9uZ19sYWJlbCA9IGlmZWxzZShjdWUgPT0gIlIgbG9nICYgSSBsb2ciLCAiUkJDIGxvZyAmXG5hc2V4dWFsIGlSQkMgbG9nIiwgbG9uZ19sYWJlbCksCiAgICAgICAgIHNob3J0X2xhYmVsID0gaWZlbHNlKGN1ZSA9PSAiUiBsb2cgJiBJIGxvZyIsICJSIGxvZyAmIEkgbG9nIiwgc2hvcnRfbGFiZWwpLAogICAgICAgICBwYXJhbWV0ZXJfbGFiZWwgPSBjYXNlX3doZW4oICMjIHJlY29kZSBwYXJhbWV0ZXIgdmFsdWVzCiAgICBwYXJhbWV0ZXIgPT0gInJobyIgfiAiUkJDIHJlcGxlbmlzaG1lbnQgKM+BKSIsCiAgICBwYXJhbWV0ZXIgPT0gInBoaW4iIH4gIkhhbGYtbGlmZSBpbmRpcyAoz5VuKSIsCiAgICBwYXJhbWV0ZXIgPT0gInBoaXciIH4gIkhhbGYtbGlmZSB0YXJnZXRlZCAoz5V3KSIsCiAgICBwYXJhbWV0ZXIgPT0gInBzaW4iIH4gIkFjdGl2YXRpb24gaW5kaXMgKM+IbikiLAogICAgcGFyYW1ldGVyID09ICJwc2l3IiB+ICJBY3RpdmF0aW9uIHRhcmdldGVkICjPiHcpIiwKICAgIHBhcmFtZXRlciA9PSAiYmV0YSIgfiAiQnVyc3Qgc2l6ZSAozrIpIiwKICApKSAjIyBtYW51YWxseSBhZGQgaW4gdGhlIGxhYmVscwoKIyMgcmVvcmRlciBwYXJhbWV0ZXIKbWNfc2luZ2xlX2ZpdG5lc3MubG9uZzIkcGFyYW1ldGVyX2xhYmVsIDwtIGZhY3RvcihtY19zaW5nbGVfZml0bmVzcy5sb25nMiRwYXJhbWV0ZXJfbGFiZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiQnVyc3Qgc2l6ZSAozrIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSQkMgcmVwbGVuaXNobWVudCAoz4EpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIYWxmLWxpZmUgaW5kaXMgKM+VbikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhhbGYtbGlmZSB0YXJnZXRlZCAoz5V3KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQWN0aXZhdGlvbiBpbmRpcyAoz4huKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQWN0aXZhdGlvbiB0YXJnZXRlZCAoz4h3KSIpKQoKIyMgZ2V0IG1lZGlhbiBmaXRuZXNzIHZhbHVlcwptY19zaW5nbGVfZml0bmVzcy5zdW0yIDwtIG1jX3NpbmdsZV9maXRuZXNzLmxvbmcyICU+JSAKICBncm91cF9ieShzaG9ydF9sYWJlbCwgcGFyYW1ldGVyX2xhYmVsKSAlPiUgCiAgc3VtbWFyaXplKG1lZGlhbiA9IG1lZGlhbih2YWx1ZSksCiAgICAgICAgICAgIG1lYW4gPSBtZWFuKHZhbHVlKSwKICAgICAgICAgICAgZ2VvX21lYW4gPSBleHAobWVhbihsb2codmFsdWUpKSkpCgojIyBnZXQgZGV0ZXJtaW5pc3RpYyBtb2RlbCBmaXRuZXNzCm1jX3NpbmdsZV9kZXQuZGYgPC0gbWNfc2luZ2xlX2ZpdG5lc3Muc3VtMiAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChzaV9vcHQuZGYsIHNob3J0X2xhYmVsLCBmaXRuZXNzX2RldCA9IGZpdG5lc3NfMjApLCBieSAgPSAic2hvcnRfbGFiZWwiKSAlPiUgCiAgbXV0YXRlKGZpdG5lc3NfZGV0ID0gaWZlbHNlKHNob3J0X2xhYmVsID09ICJSIGxvZyAmIEkgbG9nIiwgOS44NTQxNzQsIGZpdG5lc3NfZGV0KSkgIyMgbWFudWFsbHkgaW5wdXQgZGV0ZXJtaW5pc3RpYyBmaXRuZXNzIG9mIFIgbG9nIGFuZCBJIGxvZwoKIyMgZ2V0IGRhdGEgb25seSB3aXRoIDg5JSBjcmVkaWJsZSBpbnRlcnZhbAptY19zaW5nbGVfZml0bmVzcy5sb25nMl9mIDwtIG1jX3NpbmdsZV9maXRuZXNzLmxvbmcyICU+JSAKICBncm91cF9ieShsb25nX2xhYmVsLCBwYXJhbWV0ZXJfbGFiZWwpICU+JSAKICBtdXRhdGUoY2lfbG93ZXIgPSBjaSh2YWx1ZSwgbWV0aG9kID0gIkhESSIsIGNpID0gMC44OSlbWzJdXSwKICAgICAgICAgY2lfaGlnaGVyID0gY2kodmFsdWUsIG1ldGhvZCA9ICJIREkiLCBjaSA9IDAuODkpW1szXV0pICU+JSAKICBmaWx0ZXIodmFsdWUgPj0gY2lfbG93ZXIgJiB2YWx1ZSA8PSBjaV9oaWdoZXIpICU+JSAKICBsZWZ0X2pvaW4obWNfc2luZ2xlX2RldC5kZiwgYnkgPSBjKCJzaG9ydF9sYWJlbCIsICJwYXJhbWV0ZXJfbGFiZWwiKSkKYGBgCgojIHBsb3QKYGBge3J9CmdncGxvdCgpICsKICBnZW9tX3Zpb2xpbihkYXRhID0gbWNfc2luZ2xlX2ZpdG5lc3MubG9uZzJfZiwKICAgICAgICAgICAgICBhZXMoeCA9IGZjdF9yZW9yZGVyKHNob3J0X2xhYmVsLCBmaXRuZXNzX2RldCwgLmRlc2MgPSBUKSwgeSA9IHZhbHVlKSwgCiAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodCBncmV5IiwgdHJpbSA9IFQpICsKICBnZW9tX3BvaW50KGRhdGEgPSAgbWNfc2luZ2xlX2RldC5kZiwKICAgICAgICAgICAgIGFlcyh4ID0gc2hvcnRfbGFiZWwsIHkgPSBtZWFuLCBzaGFwZSA9ICJNZWFuIiwgY29sb3IgPSAiTWVhbiIpLCBzaXplID0gMikgKwogIGdlb21fcG9pbnQoZGF0YSA9ICBtY19zaW5nbGVfZGV0LmRmLAogICAgICAgICAgICAgYWVzKHggPSBzaG9ydF9sYWJlbCwgeSA9IGdlb19tZWFuLCBzaGFwZSA9ICJHZW9tZXRyaWMgbWVhbiIsIGNvbG9yID0gIkdlb21ldHJpYyBtZWFuIiksIHNpemUgPSAyKSArCiAgZmFjZXRfd3JhcCh+cGFyYW1ldGVyX2xhYmVsKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSAgbWNfc2luZ2xlX2RldC5kZiwKICAgICAgICAgICAgIGFlcyh4ID0gc2hvcnRfbGFiZWwsIHkgPSBmaXRuZXNzX2RldCwgc2hhcGUgPSAiRGV0ZXJtaW5pc3RpYyIsIGNvbG9yID0gIkRldGVybWluaXN0aWMiKSwgc2l6ZSA9MikgKwogIGxhYnMoeCA9ICJDdWUocykiLCB5ID0gIkZpdG5lc3MiLCBzaGFwZSA9ICJMZWdlbmQiLCBjb2xvciA9ICJMZWdlbmQiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgYmx1ZSwgIiNGODc2NkQiKSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSxoanVzdD0xKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpIAoKZ2dzYXZlKHVuaXRzID0gInB4IiwgZHBpID0gMzAwLCB3aWR0aCA9IDIwMDAsIGhlaWdodCA9IDE1MDAsIGZpbGVuYW1lID0gaGVyZSgiY29kZV9yZXBvc2l0b3J5L2ZpZ3VyZXMvbWNfc2luZ2xlX2ZpdG5lc3MudGlmZiIpLCBiZyA9ICJ3aGl0ZSIsIHNjYWxlID0gMSkKYGBgCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgRGVjaXBoZXJpbmcgd2h5IGNlcnRhaW4gY3VlcyBhcmUgbGVzcyBzdXNjZXB0aWJsZSB0byBidXJzdCBzaXplIHZhcmlhdGlvbgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIy0tLS0tLS0tLS0tLS0tLSBPcHRpbWl6ZWQgdnMgbm9uLW9wdGltaXplZCBmaXRuZXNzIGFnYWluc3QgYnVyc3Qgc2l6ZSB2YXJpYXRpb24tLS0tLS0tLS0tLS0tIwpUaGlzIGNvdWxkIGJlIGF0dHJpYnV0ZWQgdG8gbG93ZXIgYWJpbGl0eSBvZiBjdWVzIChldmVuIHdpdGggb3B0aW1hbCByZWFjdGlvbiBub3JtKSB0byByZXNwb25kIHRvIGNoYW5nZWQgYmV0YS4gT3IgaXQgY291bGQgYmUgcm9idXN0bmVzcyEKIyMgUHJvY2VzcyBkYXRhIGZvciBkb3duc3RyZWFtIGFuYWx5c2lzIChleGVjdXRlIG9uY2UpCmBgYHtyfQojIyBnZXQgbGlzdCBvZiBmaWxlcwptY19idXJzdC5scyA8LSBsaXN0LmZpbGVzKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX2J1cnN0X29wdC8iKSwgcGF0dGVybiA9ICIqLmNzdiIsIGZ1bGwubmFtZXMgPSBUKQoKIyMgcmVhZCBpbiBhbmQgYmluZC4gTm90ZSB0aGF0IHdlIGFyZSBleGNsdWRpbmcgdGhlIHBhcmFtZXRlciB2YWx1ZXMgZm9yIG5vdyBiZWNhdXNlIFIgbG9nICsgSSBsb2cgaGFzIDkgb2YgdGhlbQptY19idXJzdC5kZl9yYXcgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KG1jX2J1cnN0LmxzLCBmdW5jdGlvbih4KXsKICBkZiA8LSByZWFkLmNzdih4KQogIGRmX3AgPC0gZGYgJT4lIHNlbGVjdChpZCwgY3VlLCBsb2csIGJldGEsIGZpdG5lc3MsIGRlZmF1bHQpCiAgcmV0dXJuKGRmX3ApfSkpCgojIyB3ZSBub3RlIHRoYXQgdGhlIFIgbG9nICsgSSBsb2cgaGFzIGEgc2xpZ2h0bHkgaGlnaGVyIGZpdG5lc3MgdGhhbiB0aW1lIChkZiA9IDMpLCB3aGljaCBpcyBkdWUgdG8gdGhlIGdyZWF0ZXIgZmxleGliaWxpdHkgb2Ygc3BsaW5lLiBOT3RlIGluIHByZXZpb3VzIG9wdGltaXphdGlvbiwgaW5jcmVhc2luZyB0aGUgZGYgPSA5IGFsbG93ZWQgdGltZSBiYXNlZCBjdWUgdG8gYWNoaWV2ZSBoaWdoZXIgZml0bmVzcyB0aGFuIFIgbG9nICsgSSBsb2cKbWNfYnVyc3QuZGZfcmF3ICU+JSBmaWx0ZXIoaWQgPT0gIlJfbG9nK0lfbG9nIikKCiMjIHdlIG9wdGltaXplZCBlYWNoIGN1ZSAoYW5kIGJ1cnN0IHNpemUpIHZpYSAyIG1ldGhvZHM6IG9uZSBzdGFydGluZyBmcm9tIHRoZSBvcHRpbWFsIHBhcmFtZXRlciBzaXplIHdoZW4gYmV0YSA9IDUuNzIxIChkZWZhdWx0ID0gRikgYW5kIG9uZSBzdGFydGluZyBhdCAoMC41eDQpIChkZWZhdWx0ID0gVCkuIFdlIGFyZSBnb2luZyB0byBwaWNrIHRoZSB3aGljaGV2ZXIgb25lIG9mIHRoZW0gZ2F2ZSB0aGUgaGlnaGVzdCBmaXRuZXNzCm1jX2J1cnN0LmRmIDwtIG1jX2J1cnN0LmRmX3JhdyAlPiUgCiAgZmlsdGVyKGlkICE9ICJ0aW1lIikgJT4lICMjIHRpbWUgaXMgaWdub3JlZCBoZXJlIGJlY2F1c2UgaXJyZWxhdmFuY2UKICBncm91cF9ieShpZCwgYmV0YSkgJT4lIAogIHRvcF9uKDEsIGZpdG5lc3MpICU+JSAjIyMgcGljayB0aGUgb25lIHdpdGggdGhlIGhpZ2hlc3QgZml0bmVzcwogIG11dGF0ZShsb2cgPSBpZmVsc2UobG9nID09ICJsb2cxMCIsICJsb2ciLCAibm9uZSIpKSAjIyBjaGFuZ2UgbG9nIGNsYXNzaWZpY2F0aW9uIGZvciBjb25zaXN0ZW5jeQoKIyMgd3JpdGUgCndyaXRlLmNzdihtY19idXJzdC5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvbWNfYnVyc3Rfb3B0LmNzdiIpKQpgYGAKCiMjIFByb2Nlc3MgZGF0YSBmb3IgcGxvdHRpbmcKYGBge3J9CiMjIGxpc3Qgb2YgYnVyc3Qgc2l6ZSB1c2VkIGluIHRoZSBvcHRpbWl6YXRpb24gc2ltdWxhdGlvbnMKYnVyc3RfbHMgPC0gYygzLjk4LDQuNyw1LjUzLCA2LjA4KQoKIyMgaGVyZSwgd2Ugd2FudCB0byBzZWUgaWYgYWZ0ZXIgb3B0aW1pemF0aW9uLCB0aGUgb3B0aW1pemVkIGZpdG5lc3MgdmFsdWVzIGNvcnJlbGF0ZWQgd2l0aCB1bm9wdGltaXplZCBmaXRuZXNzIHZhbHVlcy4gVGhhdCBpcywgaXMgdGhlIGRlY2xpbmUgb2YgZml0bmVzcyBjYXVzZWQgYnkgYnVyc3Qgc2l6ZSB2YXJpYXRpb24gZHVlIHRvIGxpbWl0YXRpb25zIG9mIHRoZSBjdWUgdG8gYWNoaWV2ZSBoaWdoZXIgZml0bmVzcyBvciByb2J1c3RuZXNzIG9mIHRoZSBjdWU/CnBvc3Rlcmlvcl9idXJzdF9pdGVyIDwtIHBvc3Rlcmlvci5kZiAlPiUgCiAgZmlsdGVyKHJvdW5kKGJ1cnN0LCBkaWdpdHMgPSAyKSAlaW4lIGJ1cnN0X2xzKSAlPiUgIyMgZ2V0IGl0ZXJhdGlvbnMgdGhhdCBoYXMgYSBidXJzdCB2YWx1ZSBjbG9zZSB0byB0aGUgb25lIHdlIHRlc3RlZAogIG11dGF0ZShkaWZmXzMgPSBhYnMoYnVyc3QgLSBidXJzdF9sc1tbMV1dKSwKICAgICAgICAgZGlmZl80ID0gYWJzKGJ1cnN0IC0gYnVyc3RfbHNbWzJdXSksCiAgICAgICAgIGRpZmZfNSA9IGFicyhidXJzdCAtIGJ1cnN0X2xzW1szXV0pLAogICAgICAgICBkaWZmXzYgPSBhYnMoYnVyc3QgLSBidXJzdF9sc1tbNF1dKSkKCiMjIGdldCBpdGVyYXRpb25zIHRoYXQgd2Ugc2hvdWxkIHRha2UgZml0bmVzcyB2YWx1ZXMgZnJvbQpidXJzdF9pdGVyXzMgPC0gcG9zdGVyaW9yX2J1cnN0X2l0ZXJbcG9zdGVyaW9yX2J1cnN0X2l0ZXIkZGlmZl8zID09IG1pbihwb3N0ZXJpb3JfYnVyc3RfaXRlciRkaWZmXzMpLCAiaWQiXQpidXJzdF9pdGVyXzQgPC0gcG9zdGVyaW9yX2J1cnN0X2l0ZXJbcG9zdGVyaW9yX2J1cnN0X2l0ZXIkZGlmZl80ID09IG1pbihwb3N0ZXJpb3JfYnVyc3RfaXRlciRkaWZmXzQpLCAiaWQiXQpidXJzdF9pdGVyXzUgPC0gcG9zdGVyaW9yX2J1cnN0X2l0ZXJbcG9zdGVyaW9yX2J1cnN0X2l0ZXIkZGlmZl81ID09IG1pbihwb3N0ZXJpb3JfYnVyc3RfaXRlciRkaWZmXzUpLCAiaWQiXQpidXJzdF9pdGVyXzYgPC0gcG9zdGVyaW9yX2J1cnN0X2l0ZXJbcG9zdGVyaW9yX2J1cnN0X2l0ZXIkZGlmZl82ID09IG1pbihwb3N0ZXJpb3JfYnVyc3RfaXRlciRkaWZmXzYpLCAiaWQiXQoKYnVyc3RfaXRlcl9scyA8LSBjKGJ1cnN0X2l0ZXJfMywgYnVyc3RfaXRlcl80LCBidXJzdF9pdGVyXzUsIGJ1cnN0X2l0ZXJfNikKCiMjIGdldCBqb2luaW5nIGRhdGFmcmFtZSB0byBhc3NvY2lhdGUgaXRlcmF0aW9uIG51bWJlciBvZiBidXJzdCBzaXplCmJ1cnN0X2l0ZXIuZGYgPC0gcG9zdGVyaW9yLmRmICU+JSAKICBmaWx0ZXIoaWQgJWluJSBjKCBidXJzdF9pdGVyX2xzKSkgJT4lIAogIG11dGF0ZShidXJzdF9yb3VuZCA9IHJvdW5kKGJ1cnN0LCBkaWdpdHMgPSAyKSkgJT4lIAogIHNlbGVjdChpdGVyID0gaWQsIGJldGEgPSBidXJzdF9yb3VuZCkKCm1jX2J1cnN0X2ZpdG5lc3NfZmluYWwuZGYgPC0gbWNfc2luZ2xlX2ZpdG5lc3MubG9uZyAlPiUgCiAgZmlsdGVyKHBhcmFtZXRlciA9PSAiYmV0YSIgJiBpdGVyICVpbiUgYyhidXJzdF9pdGVyX2xzKSkgJT4lICMjIGtlZXAgb25seSByb3dzIHdoZXJlIHRoZSBwYXJhbWV0ZXIgdmFsdWUgYWx0ZXJlZCBpcyBiZXRhIGFuZCBiZWxvbmdzIHRvIHRoZSBhcHByb3ByaWF0ZSBpdGVyYXRpb25zIAogIGxlZnRfam9pbihidXJzdF9pdGVyLmRmLCBieSA9ICJpdGVyIikgJT4lICMjIGdldCBiZXRhIHZhbHVlIGFzc29jaWF0ZWQgd2l0aCBpdGVyYXRpb25zCiAgc2VsZWN0KGN1ZSwgbG9nLCBmaXRuZXNzX3Vub3B0ID0gdmFsdWUsIGJldGEpICU+JSAjIyBzZWxlY3QgcmVsZXZhbnQgY29sdW1ucwogIGxlZnRfam9pbihzZWxlY3QobWNfYnVyc3QuZGYsIGN1ZSwgbG9nLCBiZXRhLCBmaXRuZXNzX29wdCA9IGZpdG5lc3MpLCBieSA9IGMoImN1ZSIsICJsb2ciLCAiYmV0YSIpKSAlPiUgICMjIGpvaW4gdG8gZ2V0IGZpdG5lc3MgdmFsdWUgYWZ0ZXIgb3B0aWl6YXRpb24gYmFzZWQgb24gY3VlLCBsb2cgc3RhdHVzLCBhbmQgYmV0YSB2YWx1ZQogIGxlZnRfam9pbihzZWxlY3QoZXpfbGFiZWwsIGN1ZSwgbG9nLCBzaG9ydF9sYWJlbCwgbG9uZ19sYWJlbCksIGJ5ID0gYygiY3VlIiwgImxvZyIpKSAlPiUgCiAgbXV0YXRlKHNob3J0X2xhYmVsID0gaWZlbHNlKGN1ZSA9PSAiUiBsb2cgJiBJIGxvZyIsIGN1ZSwgc2hvcnRfbGFiZWwpKSAlPiUgICMjIG1hbnVhbGx5IGdlbmVyYXRlIGxhYmVsIGZvciBSIGxvZyBhbmQgSSBsb2cKICBwaXZvdF9sb25nZXIoY29scyA9IGMoZml0bmVzc191bm9wdCwgZml0bmVzc19vcHQpKSAlPiUgIyMgbWFrZSBsb25nCiAgI3NlbGVjdCgtaWQpICU+JSAKICBtdXRhdGUoY2xhc3NpZmljYXRpb24gPSBpZmVsc2UobmFtZSA9PSAiZml0bmVzc191bm9wdCIsICJVbm9wdGltaXplZCIsICJPcHRpbWl6ZWQiKSkKCiMjIGdldCBtZWFuIHZhbHVlcwptY19idXJzdF9maXRuZXNzX21lZC5kZiA8LSBtY19idXJzdF9maXRuZXNzX2ZpbmFsLmRmICU+JSAKICBncm91cF9ieShjbGFzc2lmaWNhdGlvbiwgc2hvcnRfbGFiZWwpICU+JSAKICBtdXRhdGUobWVhbiA9IG1lYW4odmFsdWUpLAogICAgICAgICBnZW9tX21lYW4gPSBleHAobWVhbihsb2codmFsdWUpKSkpICU+JQogIGRpc3RpbmN0KGN1ZSwgbG9nLCAua2VlcF9hbGwgPSBUKSAlPiUgCiAgdW5ncm91cCgpCmBgYAoKIyMgcGxvdApgYGB7cn0KIyMgbm90ZSB0aGF0IGFsbCBwb2ludHMgYXJlIHJhbmtlZCBieSBpbmNyZWFzaW5nIGdlb21ldHJpYyBtZWFuIHdpdGhpbiBlYWNoIGZhY2V0cyEKbWNfYnVyc3RfZml0bmVzcy5wbCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBtY19idXJzdF9maXRuZXNzX2ZpbmFsLmRmLCBhZXMoeCA9IHRpZHl0ZXh0OjpyZW9yZGVyX3dpdGhpbihzaG9ydF9sYWJlbCwgdmFsdWUsIGNsYXNzaWZpY2F0aW9uLCBmdW4gPSBmdW5jdGlvbih4KSBleHAobWVhbihsb2coeCkpKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSB2YWx1ZSwgZ3JvdXAgPSBiZXRhKSwgY29sb3IgPSAiZGFyayBncmV5IikgKwogIGdlb21fcG9pbnQoZGF0YSA9IG1jX2J1cnN0X2ZpdG5lc3NfZmluYWwuZGYsIGFlcyh4ID0gdGlkeXRleHQ6OnJlb3JkZXJfd2l0aGluKHNob3J0X2xhYmVsLCB2YWx1ZSwgY2xhc3NpZmljYXRpb24sIGZ1biA9IGZ1bmN0aW9uKHgpIGV4cChtZWFuKGxvZyh4KSkpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSB2YWx1ZSwgY29sb3IgPSBiZXRhKSwgc2l6ZSA9IDIuNSwgYWxwaGEgPSAwLjYpICsKICBnZW9tX2xpbmUoZGF0YSA9IG1jX2J1cnN0X2ZpdG5lc3NfbWVkLmRmLCBhZXMoeCA9IHRpZHl0ZXh0OjpyZW9yZGVyX3dpdGhpbihzaG9ydF9sYWJlbCwgdmFsdWUsIGNsYXNzaWZpY2F0aW9uLCBmdW4gPSBmdW5jdGlvbih4KSBleHAobWVhbihsb2coeCkpKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZ2VvbV9tZWFuLCBncm91cCA9IGNsYXNzaWZpY2F0aW9uKSwgc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IG1jX2J1cnN0X2ZpdG5lc3NfbWVkLmRmLCBhZXMoeCA9IHRpZHl0ZXh0OjpyZW9yZGVyX3dpdGhpbihzaG9ydF9sYWJlbCwgdmFsdWUsIGNsYXNzaWZpY2F0aW9uLCBmdW4gPSBmdW5jdGlvbih4KSBleHAobWVhbihsb2coeCkpKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGdlb21fbWVhbiksIHNpemUgPSAzLCBzaGFwZSA9IDE3LCBjb2xvciA9ICJibGFjayIpICsKICBmYWNldF93cmFwKH5jbGFzc2lmaWNhdGlvbiwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlIikgKwogIHRpZHl0ZXh0OjpzY2FsZV94X3Jlb3JkZXJlZCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgbGFicyh4ID0gIkN1ZShzKSIsIHkgPSAiRml0bmVzcyIsIGNvbG9yID0gIkJ1cnN0IHNpemUiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0PTEpKQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0gVGhlIGFkdmFudGFnZSBvZiBJIGxvZyBpcyBub3QgZHVlIHRvIGRlZmF1bHQgb3B0aW1pemF0aW9uIHBvc2l0aW9uLS0tLS0tLS0tLS0tLS0tLSMKSGVyZSwgd2Ugd2lsbCB1c2UgdGhlIDQgb3B0aW1pemVkIHBhcmFtZXRlciBhdCB2YXJpb3VzIHRpbWUgcG9pbnQgYW5kIG9idGFpbiB0aGVpciBmaXRuZXNzIGFjcm9zcyB0aGUgMyBvdGhlciBidXJzdCB2YWx1ZXMKIyMgZnVuY3Rpb24gdG8gZ2V0IGZpdG5lc3MgYW5kIGNvbnZlcnNpb24gcmF0ZSAKYGBge3J9CmdldF9tY19maXRuZXNzX2RpZmYgPC0gZnVuY3Rpb24oZGYsIGR1YWwgPSBGKXsKICAjIyBsaXN0IG9mIGJ1cnN0IHZhbHVlcyB0byB0ZXN0CiAgYnVyc3RfbHMgPC0gYygzLjk4LDQuNyw1LjUzLCA1LjcyMSwgNi4wOCkKICAKICAjIyBnZXQgcGFyYW1ldGVyIHNldAogICMjIyBhc3NpZ24gdGhlIGJhc2VsaW5lIHBhcmFtZXRlciBzZXQKICBwYXJhbWV0ZXJzX3RzdWt1c2hpX2RlZmF1bHQgPC0gYyhSMSA9IDguODkqMTBeNiwgCiAgICAgICAgICAgICAgICBsYW1iZGEgPSAzLjcqMTBeNSwKICAgICAgICAgICAgICAgIG11ID0gMC4wMjUsIAogICAgICAgICAgICAgICAgcCA9IDgqMTBeLTYsIAogICAgICAgICAgICAgICAgYWxwaGEgPSAxLCAKICAgICAgICAgICAgICAgIGFscGhhZyA9IDIsIAogICAgICAgICAgICAgICAgYmV0YSA9IGRmJGJldGEsICMjIGJldGEgaXMgaW5zZXJ0ZWQgaGVyZSAocGxhY2UgaG9sZGVyKQogICAgICAgICAgICAgICAgbXVtID0gNDgsIAogICAgICAgICAgICAgICAgbXVnID0gNCwgCiAgICAgICAgICAgICAgICBJMCA9IDQzLjg1OTY1LCAKICAgICAgICAgICAgICAgIElnMCA9IDAsIAogICAgICAgICAgICAgICAgYSA9IDE1MCwgCiAgICAgICAgICAgICAgICBiID0gMTAwLCAKICAgICAgICAgICAgICAgIHNwID0gMSwKICAgICAgICAgICAgICAgIHBzaW4gPSAxNi42OTIzNCwKICAgICAgICAgICAgICAgIHBzaXcgPSAwLjg0MzE3ODUsCiAgICAgICAgICAgICAgICBwaGluID0gMC4wMzUyMDU5MSwgCiAgICAgICAgICAgICAgICBwaGl3ID0gNTUwLjg0MiwKICAgICAgICAgICAgICAgIGlvdGEgPSAyLjE4KigxMF42KSwKICAgICAgICAgICAgICAgIHJobyA9IDAuMjYyNzE1NikKICAKICAjIyMgZ2V0IGxpc3Qgb2YgcGFyYW1hdGVycyB3aXRoIGFsbCA0IHZhbHVlcyBvZiBiZXRhCiAgcGFyYW1ldGVyX3RzdWt1c2hpX2xzIDwtIGxhcHBseShidXJzdF9scywgZnVuY3Rpb24oeCkgcmVwbGFjZShwYXJhbWV0ZXJzX3RzdWt1c2hpX2RlZmF1bHQsIDcsIHgpKQogIAogICMjIHJlYWQgaW4gaW5wdXQgZm9yIG1vZGVsIHNpbXVsYXRpb24KICBpZihpc0ZBTFNFKGR1YWwpKXsKICAgIHBhciA8LSBjKGRmJHBhcjEsIGRmJHBhcjIsIGRmJHBhcjMsIGRmJHBhcjQpICMjIHBhcmFtZXRlciBzZXQKICAgIGN1ZSA8LSBkZiRjdWUKICAgIGxvZyA8LSBkZiRsb2cKICAgIGN1ZV9yYW5nZSA8LSBzZXEoZGYkbG93LCBkZiRoaWdoLCBkZiRieSkKICB9IGVsc2V7CiAgICBwYXIgPC0gYyhkZiRwYXIxLCBkZiRwYXIyLCBkZiRwYXIzLCBkZiRwYXI0LCBkZiRwYXI1LCBkZiRwYXI2LCBkZiRwYXI3LCBkZiRwYXI4LCBkZiRwYXI5KQogICAgY3VlIDwtICJSIgogICAgY3VlX2IgPC0gIkkiCiAgICBsb2cgPC0gImxvZzEwIgogICAgbG9nX2IgPC0gImxvZzEwIgogICAgY3VlX3JhbmdlIDwtIHNlcSg2LCA3LCBsZW5ndGgub3V0ID0gNTAwKQogICAgY3VlX3JhbmdlX2IgPC0gc2VxKDAsCTYuNzc4MTUxMjUsIGxlbmd0aC5vdXQgPSA1MDApCiAgfQogIAogICMjIGdldCBkeW5hbWljcyBkYXRhCiAgaWYoaXNGQUxTRShkdWFsKSl7CiAgICBmaXRuZXNzX2xzIDwtIGxhcHBseShwYXJhbWV0ZXJfdHN1a3VzaGlfbHMsIGZ1bmN0aW9uKHgpewogICAgICBkeW4gPC0gY2hhYmF1ZGlfc2lfY2xlYW4ocGFyYW1ldGVyc19jciA9IHBhciwgCiAgICAgIHBhcmFtZXRlcnMgPSB4LCAKICAgICAgaW1tdW5pdHkgPSAidHN1a3VzaGkiLAogICAgICB0aW1lX3JhbmdlID0gc2VxKDAsIDIwLCAwLjAxKSwgCiAgICAgIGN1ZSA9IGN1ZSwgCiAgICAgIGxvZ19jdWUgPSBsb2csIAogICAgICBjdWVfcmFuZ2UgPSBjdWVfcmFuZ2UsIAogICAgICBzb2x2ZXIgPSAidm9kZSIsCiAgICAgIGR5biA9IFQpCiAgICAgICMjIyBwcm9jZXNzIGR5bmFtaWNzIGRhdGFmcmFtZSB0byBhZGQgYmV0YSwgY3VlLCBhbmQgbG9nCiAgICAgIGR5bl9wIDwtIGR5biAlPiUgbXV0YXRlKGlkID0gZGYkaWQsIGJldGFfb3B0aW1pemVkID0gZGYkYmV0YSwgYmV0YV9zaW11bGF0ZWQgPSB4W1s3XV0pCiAgICAgIHdyaXRlX3BhcnF1ZXQoZHluX3AsIGhlcmUocGFzdGUwKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19idXJzdF9keW4vIiwgZGYkaWQsICJfIiwgZ3N1YigiXFwuIiwgIi0iLCBkZiRiZXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIl8iLCBnc3ViKCJcXC4iLCAiLSIsIHhbWzddXSksICJfZHluLnBhcnF1ZXQiKSkpCiAgICAgICMjIyBnZXQgZml0bmVzcwogICAgICBmaXRuZXNzIDwtIG1heChkeW5bZHluJHZhcmlhYmxlID09ICJ0YXVfY3VtIiwgM10sIG5hLnJtID0gVCkKICAgICAgZml0bmVzc19iZXRhIDwtIGMoYmV0YV9zaW11bGF0ZWQgPSB4W1s3XV0sIGZpdG5lc3MgPSBmaXRuZXNzKSAjIyBhdHRhY2ggdGhlIGJldGEgdXNlZCBpbiB0aGUgc2ltdWxhdGlvbiBoZXJlCiAgICAgIHJldHVybihmaXRuZXNzX2JldGEpCiAgICB9KQogIH0gZWxzZXsgIyMgZHVhbCBjdWUgbW9kZWxzCiAgICBmaXRuZXNzX2xzIDwtIGxhcHBseShwYXJhbWV0ZXJfdHN1a3VzaGlfbHMsIGZ1bmN0aW9uKHgpewogICAgICBkeW4gPC0gY2hhYmF1ZGlfc2lfY2xlYW4ocGFyYW1ldGVyc19jciA9IHBhciwgCiAgICAgIHBhcmFtZXRlcnMgPSB4LAogICAgICBpbW11bml0eSA9ICJ0c3VrdXNoaSIsCiAgICAgIHRpbWVfcmFuZ2UgPSBzZXEoMCwgMjAsIDAuMDEpLCAKICAgICAgY3VlID0gY3VlLCAKICAgICAgY3VlX2IgPSBjdWVfYiwKICAgICAgbG9nX2N1ZSA9IGxvZywKICAgICAgbG9nX2N1ZV9iID0gbG9nX2IsCiAgICAgIGN1ZV9yYW5nZSA9IGN1ZV9yYW5nZSwgCiAgICAgIGN1ZV9yYW5nZV9iID0gY3VlX3JhbmdlX2IsCiAgICAgIGdhbSA9ICJ0ZSIsCiAgICAgIHNvbHZlciA9ICJ2b2RlIiwKICAgICAgZHluID0gVCkKICAgICAgCiAgICAgIyMjIHByb2Nlc3MgZHluYW1pY3MgZGF0YWZyYW1lIHRvIGFkZCBiZXRhLCBjdWUsIGFuZCBsb2cKICAgICAgZHluX3AgPC0gZHluICU+JSBtdXRhdGUoaWQgPSBkZiRpZCwgYmV0YV9vcHRpbWl6ZWQgPSBkZiRiZXRhLCBiZXRhX3NpbXVsYXRlZCA9IHhbWzddXSkKICAgICAgd3JpdGVfcGFycXVldChkeW5fcCwgaGVyZShwYXN0ZTAoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX2J1cnN0X2R5bi8iLCBkZiRpZCwgIl8iLCBnc3ViKCJcXC4iLCAiLSIsIGRmJGJldGEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXyIsIGdzdWIoIlxcLiIsICItIiwgeFtbN11dKSwgIl9keW4ucGFycXVldCIpKSkKICAgICAgIyMjIGdldCBmaXRuZXNzCiAgICAgIGZpdG5lc3MgPC0gbWF4KGR5bltkeW4kdmFyaWFibGUgPT0gInRhdV9jdW0iLCAzXSwgbmEucm0gPSBUKQogICAgICAKICAgICAgZml0bmVzc19iZXRhIDwtIGMoYmV0YV9zaW11bGF0ZWQgPSB4W1s3XV0sIGZpdG5lc3MgPSBmaXRuZXNzKSAjIyBhdHRhY2ggdGhlIGJldGEgdXNlZCBpbiB0aGUgc2ltdWxhdGlvbiBoZXJlCiAgICAgIHJldHVybihmaXRuZXNzX2JldGEpCiAgICAgIH0KICAgICkKICB9CiAgCiAgIyMgYWRkIGlkZW50aWZpZXJzIHRvIHJlc3VsdHMgKGZpdG5lc3MpCiAgcmVzX2ZpbmFsIDwtIGRvLmNhbGwocmJpbmQsIGZpdG5lc3NfbHMpICU+JSAKICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgICBtdXRhdGUoaWQgPSBkZiRpZCwKICAgICAgICAgICBjdWUgPSBjdWUsCiAgICAgICAgICAgbG9nID0gbG9nLAogICAgICAgICAgIGJldGFfb3B0aW1pemVkID0gZGYkYmV0YSkKICAKICByZXR1cm4ocmVzX2ZpbmFsKQp9CmBgYAoKIyMgZ2V0IGlucHV0IGRhdGFmcmFtZSBmb3IgZnVuY3Rpb24gdG8gcnVuCmBgYHtyfQojIyBnZXQgbGlzdCBvZiBmaWxlcwptY19idXJzdC5scyA8LSBsaXN0LmZpbGVzKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX2J1cnN0X29wdC8iKSwgcGF0dGVybiA9ICIqLmNzdiIsIGZ1bGwubmFtZXMgPSBUKQptY19SSV9idXJzdC5scyA8LSBsaXN0LmZpbGVzKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX2J1cnN0X29wdC8iKSwgcGF0dGVybiA9ICJSX2xvZ19JX2xvZyoiLCBmdWxsLm5hbWVzID0gVCkKCiMjIHJlYWQgaW4gYW5kIGJpbmQuIEV4Y2x1ZGUgUiBsb2cgKyBJIGxvZyBkdWUgdG8gZGlmZmVyZW5jZXMgaW4gIyBvZiBwYXJhbWV0ZXJzCm1jX2J1cnN0X3Bhci5kZl9yYXcgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KG1jX2J1cnN0LmxzWyFncmVwbCgiUl9sb2dfSV9sb2ciLCBtY19idXJzdC5scyldLCBmdW5jdGlvbih4KXsKICBkZiA8LSByZWFkLmNzdih4KQogIGRmX3AgPC0gZGYgJT4lIHNlbGVjdChpZCwgY3VlLCBsb2csIGJldGEsIGZpdG5lc3MsIHBhcjEsIHBhcjIsIHBhcjMsIHBhcjQsIGRlZmF1bHQpCiAgcmV0dXJuKGRmX3ApfSkpCm1jX2J1cnN0X1JJLmRmX3JhdyA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkobWNfUklfYnVyc3QubHMsIGZ1bmN0aW9uKHgpewogIGRmIDwtIHJlYWQuY3N2KHgpCiAgZGZfcCA8LSBkZiAlPiUgc2VsZWN0KGlkLCBjdWUsIGxvZywgYmV0YSwgZml0bmVzcywgcGFyMSwgcGFyMiwgcGFyMywgcGFyNCwgcGFyNSwgcGFyNiwgcGFyNywgcGFyOCwgcGFyOSwgZGVmYXVsdCkKICByZXR1cm4oZGZfcCl9KSkKCiMjIGdldCB0aGUgdG9wIHN0cmF0ZWd5IGJhc2VkIG9uIGZpdG5lc3MKbWNfYnVyc3RfcGFyLmRmX2YgPC0gbWNfYnVyc3RfcGFyLmRmX3JhdyAlPiUgCiAgZ3JvdXBfYnkoaWQsIGJldGEpICU+JSAKICB0b3BfbigxLCBmaXRuZXNzKSAlPiUgCiAgZmlsdGVyKGlkICE9ICJ0aW1lIikgIyMgZ2V0IHJpZCBvZiB0aW1lCgptY19idXJzdF9SSS5kZiA8LSBtY19idXJzdF9SSS5kZl9yYXcgJT4lIAogIGdyb3VwX2J5KGlkLCBiZXRhKSAlPiUgCiAgdG9wX24oMSwgZml0bmVzcykKCiMjIGpvaW4gd2l0aCBzaV9vcHQgdG8gZ2V0IGN1ZSByYW5nZXMKbWNfYnVyc3RfcGFyLmRmX2ZwIDwtIG1jX2J1cnN0X3Bhci5kZl9mICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KHNpX29wdC5kZiwgaWQsIGxvdywgaGlnaCwgYnksIGxvbmdfbGFiZWwpLCBieSA9ICJpZCIpCgojIyB3cml0ZQp3cml0ZS5jc3YobWNfYnVyc3RfcGFyLmRmX2ZwLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19idXJzdF9zaW5nbGVfaW5wdXQuY3N2IikpCndyaXRlLmNzdihtY19idXJzdF9SSS5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvbWNfYnVyc3RfZHVhbF9pbnB1dC5jc3YiKSkKYGBgCgojIyBydW4gZnVuY3Rpb24KYGBge3J9CiMjIHNpbmdsZSBjdWVzIG9ubHkKbWNfYnVyc3Rfc2luZ2xlX2RpZmYubHMgPC0gbWNsYXBwbHkoc3BsaXQobWNfYnVyc3Rfc2luZ2xlX2lucHV0LmRmLCBzZXEobnJvdyhtY19idXJzdF9zaW5nbGVfaW5wdXQuZGYpKSksIGZ1bmN0aW9uKHgpIGdldF9tY19maXRuZXNzX2RpZmYoeCksIG1jLmNvcmVzID0gNCkKbWNfYnVyc3Rfc2luZ2xlX2RpZmYuZGYgPC0gZG8uY2FsbChyYmluZCwgbWNfYnVyc3Rfc2luZ2xlX2RpZmYubHMpCgojIyBkdWFsIGN1ZXMKbWNfYnVyc3RfUklfZGlmZi5scyA8LSBtY2xhcHBseShzcGxpdChtY19idXJzdF9kdWFsX2lucHV0LmRmLCBzZXEobnJvdyhtY19idXJzdF9kdWFsX2lucHV0LmRmKSkpLCBmdW5jdGlvbih4KSBnZXRfbWNfZml0bmVzc19kaWZmKHgsIGR1YWwgPSBUKSwgbWMuY29yZXMgPSA0KQptY19idXJzdF9SSV9kaWZmLmRmIDwtIGRvLmNhbGwocmJpbmQsIG1jX2J1cnN0X1JJX2RpZmYubHMpCgojIyBzYW5pdHkgY2hlY2suIElmIGJldGEgPSBiZXRhIHNpbXVsYXRlZCwgdGhlIHByb2R1Y2VkIGZpdG5lc3Mgc2hvdWxkIGVxdWFsIHRoZSBvcHRpbWl6ZWQgZml0bmVzcyBvYnRhaW5lZCBwcmV2aW91c2x5LgptY19idXJzdF9zaW5nbGVfZGlmZi5kZiAlPiUgCiAgZmlsdGVyKGJldGFfc2ltdWxhdGVkID09IGJldGFfb3B0aW1pemVkKSAlPiUgIyMgbG9vayBhdCBvbmx5IGluY2lkZW5jZXMgd2hlcmUgdGhlIHBhcmFtZXRlciBvcHRpbWl6ZWQgbWF0Y2hlZCB0aGUgc2ltdWxhdGlvbi4gVGhlc2Ugc2hvdWxkIHJldHVybiB0aGUgb3B0aW16aWVkIGZpdG5lc3MgdmFsdWVzCiAgbGVmdF9qb2luKHNlbGVjdChtY19idXJzdC5kZiwgaWQsIGJldGEsIGZpdG5lc3Nfb3B0aW1pemVkID0gZml0bmVzcyksIAogICAgICAgICAgICBieSA9IGMoImlkIiwgImJldGFfb3B0aW1pemVkIiA9ICJiZXRhIikpICU+JSAjIyBqb2luIHdpdGggb3B0aW1pemVkIGZpdG5lc3MgdmFsdWVzIGJhc2VkIG9uIGlkIGFuZCBiZXRhIHZhbHVlcwogIG11dGF0ZShkaWZmID0gYWJzKGZpdG5lc3Nfb3B0aW1pemVkIC0gZml0bmVzcykpICMjIGxvb2tzIGdvb2QhCm1jX2J1cnN0X1JJX2RpZmYuZGYgJT4lIAogIGZpbHRlcihiZXRhX3NpbXVsYXRlZCA9PSBiZXRhX29wdGltaXplZCkgJT4lICMjIGxvb2sgYXQgb25seSBpbmNpZGVuY2VzIHdoZXJlIHRoZSBwYXJhbWV0ZXIgb3B0aW1pemVkIG1hdGNoZWQgdGhlIHNpbXVsYXRpb24uIFRoZXNlIHNob3VsZCByZXR1cm4gdGhlIG9wdGltemllZCBmaXRuZXNzIHZhbHVlcwogIGxlZnRfam9pbihzZWxlY3QobWNfYnVyc3QuZGYsIGlkLCBiZXRhLCBmaXRuZXNzX29wdGltaXplZCA9IGZpdG5lc3MpLCAKICAgICAgICAgICAgYnkgPSBjKCJpZCIsICJiZXRhX29wdGltaXplZCIgPSAiYmV0YSIpKSAlPiUgIyMgam9pbiB3aXRoIG9wdGltaXplZCBmaXRuZXNzIHZhbHVlcyBiYXNlZCBvbiBpZCBhbmQgYmV0YSB2YWx1ZXMKICBtdXRhdGUoZGlmZiA9IGFicyhmaXRuZXNzX29wdGltaXplZCAtIGZpdG5lc3MpKQoKIyMgd3JpdGUKIyMjIGZpdG5lc3MKbWNfYnVyc3RfZmluYWxfZGlmZi5kZiA8LSByYmluZChtY19idXJzdF9zaW5nbGVfZGlmZi5kZiwgbWNfYnVyc3RfUklfZGlmZi5kZikgJT4lIAogIGxlZnRfam9pbihzZWxlY3QoZXpfbGFiZWwsIGlkLCBsb25nX2xhYmVsLCBzaG9ydF9sYWJlbCksIGJ5ID0gImlkIikgJT4lIAogIG11dGF0ZShzaG9ydF9sYWJlbCA9IGlmZWxzZShpZCA9PSAiUl9sb2crSV9sb2ciLCAiUiBsb2cgJiBJIGxvZyIsIHNob3J0X2xhYmVsKSkKd3JpdGUuY3N2KG1jX2J1cnN0X2ZpbmFsX2RpZmYuZGYsIGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX2J1cnN0X2ZpbmFsX2RpZmYuY3N2IikpCiMjIyBkeW5hbWljcwptY19idXJzdF9keW4ubHMgPC0gbGlzdC5maWxlcyhoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19idXJzdF9keW4iKSwgcGF0dGVybiA9ICIqLnBhcnF1ZXQiLCBmdWxsLm5hbWVzID0gVCkKbWNfYnVyc3RfZHluLmRmIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseShtY19idXJzdF9keW4ubHMsIHJlYWRfcGFycXVldCkpCndyaXRlX3BhcnF1ZXQobWNfYnVyc3RfZHluLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19idXJzdF9keW4ucGFycXVldCIpKQpgYGAKCiMjIHByb2Nlc3MgZGF0YSBmb3IgcGxvdHRpbmcKYGBge3J9CiMjIGdldCBnZW9tZXRyaWMgbWVhbi4gbm90ZSB0aGF0IHdlIGFyZSBpbmNsdWRpbmcgY2FzZXMgd2hlcmUgb3B0aW1pemVkIGJldGEgPSBzaW11bGF0ZWQgYmV0YSB0byBhY2NvdW50IGZvciBob3cgZ29vZCB0aGUgcm4gaXMgdW5kZXIgb3B0aW1hbCBjaXJjdW1zdGFuY2VzCm1jX2J1cnN0X2RpZmYuc3VtIDwtIG1jX2J1cnN0X2ZpbmFsX2RpZmYuZGYgJT4lIAogIGdyb3VwX2J5KHNob3J0X2xhYmVsKSAlPiUgCiAgc3VtbWFyaXNlKGdlb21fbWVhbiA9IGV4cChtZWFuKGxvZyhmaXRuZXNzKSkpKSAlPiUgCiAgYXJyYW5nZShnZW9tX21lYW4pCgojIyByZW9yZGVyIHggYXhpcyBiYXNlZCBvbiBnZW9tZXRyaWMgbWVhbgptY19idXJzdF9maW5hbF9kaWZmLmRmJHNob3J0X2xhYmVsIDwtIGZhY3RvcihtY19idXJzdF9maW5hbF9kaWZmLmRmJHNob3J0X2xhYmVsLCBtY19idXJzdF9kaWZmLnN1bSRzaG9ydF9sYWJlbCkKYGBgCgojIyBwbG90CmBgYHtyfQptY19idXJzdF9kaWZmLnBsIDwtIGdncGxvdCgpICsKICBnZW9tX3Zpb2xpbihkYXRhID0gbWNfYnVyc3RfZmluYWxfZGlmZi5kZiwgCiAgICAgICAgICAgICBhZXMoeCA9IHNob3J0X2xhYmVsLCB5ID0gZml0bmVzcyksIGNvbG9yID0gInRyYW5zcGFyZW50IiwgZmlsbCA9ICJsaWdodCBncmV5IikgKwogIGdlb21faml0dGVyKGRhdGEgPSBtY19idXJzdF9maW5hbF9kaWZmLmRmLCAKICAgICAgICAgICAgIGFlcyh4ID0gc2hvcnRfbGFiZWwsIHkgPSBmaXRuZXNzLCBjb2xvciA9IGJldGFfb3B0aW1pemVkKSwgc2l6ZSA9IDIsIGFscGhhID0gMC43LCB3aWR0aCA9IDAuMikgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICBnZW9tX2Nyb3NzYmFyKGRhdGEgPSBtY19idXJzdF9kaWZmLnN1bSwgYWVzKAogICAgeCA9IHNob3J0X2xhYmVsLCB5ID0gZ2VvbV9tZWFuLAogICAgeG1pbiA9IHNob3J0X2xhYmVsLCB4bWF4ID0gc2hvcnRfbGFiZWwsIHltaW4gPSBnZW9tX21lYW4sIHltYXggPSBnZW9tX21lYW4pLAogICAgICAgICAgICAgICAgICBzaXplPTEsY29sPSJibGFjayIsIHdpZHRoID0gLjUpICsKICBsYWJzKHggPSAiQ3VlKHMpIiwgeSA9ICJGaXRuZXNzIiwgY29sb3IgPSAiT3B0aW1pemVkXG5idXJzdCBzaXplIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3Q9MSkpCmBgYAoKIy0tLS0tLS0tLS0tLS0tLSBDaGFuZ2VzIGluIHJlYWN0aW9uIG5vcm0gaW4gcmVzcG9uc2UgdG8gYnVyc3Qgc2l6ZSB2YXJpYXRpb24tLS0tLS0tLS0tLS0tIwojIyBGdW5jdGlvbiB0byBvYnRhaW4gZHluYW1pY3MgZGF0YSAocnVnKSBmb3IgcGFyYXNpdGVzIGFkb3B0aW5nIHRoZSBvcHRpbWFsIHN0cmF0ZWd5IGZvciBlYWNoIGJ1cnN0IHNpemUgKGV4ZWN1dGUgb25jZSkKYGBge3J9CmdldF9tY19idXJzdF9ydWcgPC0gZnVuY3Rpb24oZGYsIGR1YWwgPSBGKXsKICAjIyBnZXQgcGFyYW1hdGVyIHNldAogIHBhcmFtZXRlcnNfdHN1a3VzaGlfYnVyc3QgPC0gYyhSMSA9IDguODkqMTBeNiwgCiAgICAgICAgICAgICAgICBsYW1iZGEgPSAzLjcqMTBeNSwKICAgICAgICAgICAgICAgIG11ID0gMC4wMjUsIAogICAgICAgICAgICAgICAgcCA9IDgqMTBeLTYsIAogICAgICAgICAgICAgICAgYWxwaGEgPSAxLCAKICAgICAgICAgICAgICAgIGFscGhhZyA9IDIsIAogICAgICAgICAgICAgICAgYmV0YSA9IGRmJGJldGEsICMjIGJldGEgaXMgaW5zZXJ0ZWQgaGVyZQogICAgICAgICAgICAgICAgbXVtID0gNDgsIAogICAgICAgICAgICAgICAgbXVnID0gNCwgCiAgICAgICAgICAgICAgICBJMCA9IDQzLjg1OTY1LCAKICAgICAgICAgICAgICAgIElnMCA9IDAsIAogICAgICAgICAgICAgICAgYSA9IDE1MCwgCiAgICAgICAgICAgICAgICBiID0gMTAwLCAKICAgICAgICAgICAgICAgIHNwID0gMSwKICAgICAgICAgICAgICAgIHBzaW4gPSAxNi42OTIzNCwKICAgICAgICAgICAgICAgIHBzaXcgPSAwLjg0MzE3ODUsCiAgICAgICAgICAgICAgICBwaGluID0gMC4wMzUyMDU5MSwgCiAgICAgICAgICAgICAgICBwaGl3ID0gNTUwLjg0MiwKICAgICAgICAgICAgICAgIGlvdGEgPSAyLjE4KigxMF42KSwKICAgICAgICAgICAgICAgIHJobyA9IDAuMjYyNzE1NikKICAKICAjIyByZWFkIGluIGlucHV0CiAgaWYoaXNGQUxTRShkdWFsKSl7CiAgICBwYXIgPC0gYyhkZiRwYXIxLCBkZiRwYXIyLCBkZiRwYXIzLCBkZiRwYXI0KSAjIyBwYXJhbWV0ZXIgc2V0CiAgICBjdWUgPC0gZGYkY3VlCiAgICBsb2cgPC0gZGYkbG9nCiAgICBjdWVfcmFuZ2UgPC0gc2VxKGRmJGxvdywgZGYkaGlnaCwgZGYkYnkpCiAgfSBlbHNlewogICAgcGFyIDwtIGMoZGYkcGFyMSwgZGYkcGFyMiwgZGYkcGFyMywgZGYkcGFyNCwgZGYkcGFyNSwgZGYkcGFyNiwgZGYkcGFyNywgZGYkcGFyOCwgZGYkcGFyOSkKICAgIGN1ZSA8LSAiUiIKICAgIGN1ZV9iIDwtICJJIgogICAgbG9nIDwtICJsb2cxMCIKICAgIGxvZ19iIDwtICJsb2cxMCIKICAgIGN1ZV9yYW5nZSA8LSBzZXEoNiwgNywgbGVuZ3RoLm91dCA9IDUwMCkKICAgIGN1ZV9yYW5nZV9iIDwtIHNlcSgwLAk2Ljc3ODE1MTI1LCBsZW5ndGgub3V0ID0gNTAwKQogIH0KICAKICAjIyBnZXQgZHluYW1pY3MgZGF0YQogIGlmKGlzRkFMU0UoZHVhbCkpewogICAgZHluIDwtIGNoYWJhdWRpX3NpX2NsZWFuKHBhcmFtZXRlcnNfY3IgPSBwYXIsIAogICAgcGFyYW1ldGVycyA9IHBhcmFtZXRlcnNfdHN1a3VzaGlfYnVyc3QsIAogICAgaW1tdW5pdHkgPSAidHN1a3VzaGkiLAogICAgdGltZV9yYW5nZSA9IHNlcSgwLCAyMCwgMC4wMSksIAogICAgY3VlID0gY3VlLCAKICAgIGxvZ19jdWUgPSBsb2csIAogICAgY3VlX3JhbmdlID0gY3VlX3JhbmdlLCAKICAgIGR5biA9IFQpCiAgfSBlbHNlewogICAgZHluIDwtIGNoYWJhdWRpX3NpX2NsZWFuKHBhcmFtZXRlcnNfY3IgPSBwYXIsIAogICAgcGFyYW1ldGVycyA9IHBhcmFtZXRlcnNfdHN1a3VzaGlfYnVyc3QsIAogICAgaW1tdW5pdHkgPSAidHN1a3VzaGkiLAogICAgdGltZV9yYW5nZSA9IHNlcSgwLCAyMCwgMC4wMSksIAogICAgY3VlID0gY3VlLCAKICAgIGN1ZV9iID0gY3VlX2IsCiAgICBsb2dfY3VlID0gbG9nLAogICAgbG9nX2N1ZV9iID0gbG9nX2IsCiAgICBjdWVfcmFuZ2UgPSBjdWVfcmFuZ2UsIAogICAgY3VlX3JhbmdlX2IgPSBjdWVfcmFuZ2VfYiwKICAgIGdhbSA9ICJ0ZSIsCiAgICBkeW4gPSBUKQogIH0KICAKICAjIyBnZXQgcnVnIGRhdGEgb25seS4gTm90ZSB0aGF0IGZvciBkdWFsIGN1ZSBtb2RlbHMgd2UgYXJlIGZpbHRlcmluZyBvdXQgUiBhbmQgSSAKICAjIyMgZmlsdGVyIHRvIGdldCBvbmx5IHRoZSByZWxldmFudCBjb2x1bW5zCiAgaWYoaXNGQUxTRShkdWFsKSl7CiAgICBpZihjdWUgIT0gIkkrSWciKXsKICAgIGR5bl9mIDwtIGR5biAlPiUgZmlsdGVyKHZhcmlhYmxlID09IGN1ZSkKICB9IGVsc2V7CiAgICBkeW5fZiA8LSBkeW4gJT4lIAogICAgICBmaWx0ZXIodmFyaWFibGUgJWluJSBjKCJJIiwgIklnIikpICU+JSAKICAgICAgZ3JvdXBfYnkodGltZSkgJT4lIAogICAgICBzdW1tYXJpc2UodmFsdWUgPSBzdW0odmFsdWUsIG5hLnJtID0gVCkpICU+JSAKICAgICAgbXV0YXRlKHZhcmlhYmxlID0gIkkrSWciKQogICAgfQogIH0gZWxzZXsKICAgIGR5bl9mIDwtIGR5biAlPiUgZmlsdGVyKHZhcmlhYmxlICVpbiUgYygiUiIsICJJIikpCiAgfQogIAogICMjIyBsb2ctdHJhbnNmb3JtIGlmIG5lZWRlZC4gVGhpcyBkb2VzIG5vdCBuZWVkZWQgdG8gYmUgYW1tZW5kZWQgZm9yIGR1YWwgY3VlIG1vZGVscyBiZWNhdXNlIGJvdGggdmFsdWUgaXMgbG9nZ2VkIGluIHRoZSBSIGxvZyAmIEkgbG9nIG1vZGVsLiBQbGVhc2UgY2hhbmdlIHRoaXMgaWYgd29ya2luZyB3aXRoIGluY2lkZW5jZXMgd2hlcmUgb25seSBvbmUgb2YgdGhlIHZhcmlhYmxlIGlzIGxvZy10cmFuc2Zvcm1lZAogIGlmKGxvZyA9PSAibG9nMTAiKXsKICAgIGR5bl9mcCA8LSBkeW5fZiAlPiUgCiAgICAgIG11dGF0ZSh2YWx1ZSA9IGxvZzEwKHZhbHVlKSwKICAgICAgICAgICAgIHZhbHVlID0gaWZlbHNlKHZhbHVlID09IC1JbmYsIDAsIHZhbHVlKSkgIyMgZm9yIC1JbmYgKHdoaWNoIGlzIHdoYXQgaGFwcGVucyB3aGVuIHlvdSBsb2cgdHJhbnNmb3JtIDApLCByZXNldCB0byAwCiAgfSBlbHNlewogICAgZHluX2ZwIDwtIGR5bl9mCiAgfQogIAogICMjIGFkZCBpZGVudGlmaWVycyB0byByZXN1bHRzCiAgZHluX2ZpbmFsIDwtIGR5bl9mcCAlPiUgCiAgICBtdXRhdGUoaWQgPSBkZiRpZCwKICAgICAgICAgICBjdWUgPSBjdWUsCiAgICAgICAgICAgbG9nID0gbG9nLAogICAgICAgICAgIGJldGEgPSBkZiRiZXRhKQogIAogIHJldHVybihkeW5fZmluYWwpCn0KYGBgCgojIyBQcmVwYXJlIHJ1ZyBhbmQgcmVhY3Rpb24gbm9ybSAoZXhlY3V0ZSBvbmNlKQpgYGB7cn0KIyMjIHNwbGl0IGRhdGFmcmFtZQptY19idXJzdF9wYXIubHNfZiA8LSBzcGxpdChtY19idXJzdF9zaW5nbGVfaW5wdXQuZGYsIHNlcShucm93KG1jX2J1cnN0X3NpbmdsZV9pbnB1dC5kZikpKQptY19idXJzdF9SSS5scyA8LSBzcGxpdChtY19idXJzdF9kdWFsX2lucHV0LmRmLCBzZXEobnJvdyhtY19idXJzdF9kdWFsX2lucHV0LmRmKSkpCgojIyBnZXQgcnVnCm1jX2J1cnN0X3J1Zy5sc19mIDwtIG1jbGFwcGx5KG1jX2J1cnN0X3Bhci5sc19mLCBnZXRfbWNfYnVyc3RfcnVnLCBtYy5jb3JlcyA9IDYpCm1jX2J1cnN0X3J1Zy5kZl9mIDwtIGRvLmNhbGwocmJpbmQsIG1jX2J1cnN0X3J1Zy5sc19mKQp3cml0ZV9wYXJxdWV0KG1jX2J1cnN0X3J1Zy5kZl9mLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19idXJzdF9ydWdfZi5wYXJxdWV0IikpCgptY19idXJzdF9SSV9ydWcubHMgPC0gbWNsYXBwbHkobWNfYnVyc3RfUkkubHMsIGZ1bmN0aW9uKHgpIGdldF9tY19idXJzdF9ydWcoeCwgZHVhbCA9IFQpLCBtYy5jb3JlcyA9IDYpCm1jX2J1cnN0X1JJX3J1Zy5kZiA8LSBkby5jYWxsKHJiaW5kLCBtY19idXJzdF9SSV9ydWcubHMpCndyaXRlX3BhcnF1ZXQobWNfYnVyc3RfUklfcnVnLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19idXJzdF9SSV9ydWcucGFycXVldCIpKQoKIyMgZ2V0IGRhdGFmcmFtZSBvZiByZWFjdGlvbiBub3JtIAojIyMgc2luZ2xlIGN1ZSBtb2RlbHMKbWNfYnVyc3Rfcm4ubHNfZiA8LSBsYXBwbHkobWNfYnVyc3RfcGFyLmxzX2YgLCBmdW5jdGlvbih4KXsKICBjdWVfcmFuZ2UgPC0gc2VxKHgkbG93LCB4JGhpZ2gsIHgkYnkpCiAgZGYgPC0gcGFyX3RvX2RmKHBhciA9IGMoeCRwYXIxLCB4JHBhcjIsIHgkcGFyMywgeCRwYXI0KSwgY3VlX3JhbmdlID0gY3VlX3JhbmdlLCBtYXggPSB4JGhpZ2gpCiAgcmVzIDwtIGRmICU+JSAKICAgIG11dGF0ZShpZCA9IHgkaWQsCiAgICAgICAgICAgY3VlID0geCRjdWUsCiAgICAgICAgICAgbG9nID0geCRsb2csCiAgICAgICAgICAgYmV0YSA9IHgkYmV0YSwKICAgICAgICAgICBmaXRuZXNzID0geCRmaXRuZXNzLAogICAgICAgICAgIGxvbmdfbGFiZWwgPSB4JGxvbmdfbGFiZWwpCiAgcmV0dXJuKHJlcykKfSkKbWNfYnVyc3Rfcm4uZGZfZiA8LSBkby5jYWxsKHJiaW5kLCBtY19idXJzdF9ybi5sc19mKQoKIyMjIER1YWwgY3VlIG1vZGVscwptY19idXJzdF9SSV9ybi5scyA8LSBsYXBwbHkobWNfYnVyc3RfUkkubHMsIGZ1bmN0aW9uKHgpewogIGN1ZV9yYW5nZSA8LSBzZXEoNiwgNywgbGVuZ3RoLm91dCA9IDUwMCkKICBjdWVfcmFuZ2VfYiA8LSBzZXEoMCwgNi43NzgxNTEyNSwgbGVuZ3RoLm91dCA9IDUwMCkKICBkZiA8LSBwYXJfdG9faG1fdGUocGFyID0gYyh4JHBhcjEsIHgkcGFyMiwgeCRwYXIzLCB4JHBhcjQsIHgkcGFyNSwgeCRwYXI2LCB4JHBhcjcsIHgkcGFyOCwgeCRwYXI5KSwgCiAgICAgICAgICAgICAgICAgIGN1ZV9yYW5nZSA9IGN1ZV9yYW5nZSwgY3VlX3JhbmdlX2IgPSBjdWVfcmFuZ2VfYikKICByZXMgPC0gZGYgJT4lIAogICAgbXV0YXRlKGlkID0geCRpZCwKICAgICAgICAgICBjdWUgPSB4JGN1ZSwKICAgICAgICAgICBsb2cgPSB4JGxvZywKICAgICAgICAgICBiZXRhID0geCRiZXRhLAogICAgICAgICAgIGZpdG5lc3MgPSB4JGZpdG5lc3MsCiAgICAgICAgICAgbG9uZ19sYWJlbCA9ICJSQkMgbG9nICZcbmFzZXh1YWwgaVJCQyBsb2ciKQogIHJldHVybihyZXMpCn0pCm1jX2J1cnN0X3JuX1JJLmRmIDwtIGRvLmNhbGwocmJpbmQsIG1jX2J1cnN0X1JJX3JuLmxzKQptY19idXJzdF9ybl9SSS5kZgoKIyMjIGdldCBib3VuZGFyeSBsaW1pdCBiYXNlZCBvbiBydWcKbWNfYnVyc3RfYm91bmQgPC0gbWNfYnVyc3RfcnVnLmRmX2YgJT4lIAogIGdyb3VwX2J5KGlkLCBiZXRhKSAlPiUgCiAgc3VtbWFyaXplKG1pbiA9IG1pbih2YWx1ZSksIG1heCA9IG1heCh2YWx1ZSkpCgptY19idXJzdF9ib3VuZF9SSSA8LSBtY19idXJzdF9SSV9ydWcuZGYgJT4lIAogIGdyb3VwX2J5KGlkLCB2YXJpYWJsZSwgYmV0YSkgJT4lIAogIHN1bW1hcml6ZShtaW4gPSBtaW4odmFsdWUpKjAuOSwgbWF4ID0gbWF4KHZhbHVlKSoxLjEpCgojIyMgZmlsdGVyIGJ5IGJvdW5kYXJ5Cm1jX2J1cnN0X3JuLmRmX2ZwIDwtIG1jX2J1cnN0X3JuLmRmX2YgJT4lIAogIGxlZnRfam9pbihtY19idXJzdF9ib3VuZCwgYnkgPSBjKCJpZCIsICJiZXRhIikpICU+JSAKICBmaWx0ZXIoY3VlX3JhbmdlID49IG1pbiAmIGN1ZV9yYW5nZSA8PSBtYXgpCgptY19idXJzdF9ybl9SSS5kZl9wIDwtIG1jX2J1cnN0X3JuX1JJLmRmICU+JSAKICBsZWZ0X2pvaW4obWNfYnVyc3RfYm91bmRfUkkgJT4lIGZpbHRlcih2YXJpYWJsZSA9PSAiUiIpLCBieSA9IGMoImlkIiwgImJldGEiKSkgJT4lIAogIGxlZnRfam9pbihtY19idXJzdF9ib3VuZF9SSSAlPiUgZmlsdGVyKHZhcmlhYmxlID09ICJJIikgJT4lIHNlbGVjdChpZCwgYmV0YSwgbWluX2IgPSBtaW4sIG1heF9iID0gbWF4KSwgYnkgPSBjKCJpZCIsICJiZXRhIikpICU+JSAKICBmaWx0ZXIoY3VlX3JhbmdlID49IG1pbiAmIGN1ZV9yYW5nZSA8PSBtYXggJiBjdWVfcmFuZ2VfYiA+PSBtaW5fYiAmIGN1ZV9yYW5nZV9iIDw9IG1heF9iKQoKIyMgd3JpdGUKd3JpdGVfcGFycXVldChtY19idXJzdF9ybi5kZl9mcCwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvbWNfYnVyc3Rfcm4ucGFycXVldCIpKQp3cml0ZV9wYXJxdWV0KG1jX2J1cnN0X3JuX1JJLmRmX3AsIGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX2J1cnN0X3JuX1JJLnBhcnF1ZXQiKSkKYGBgCgojIyBQcmVwYXJlIGRhdGEgZm9yIHBsb3R0aW5nCmBgYHtyfQojIyBnZXQgbGFiZWwgYW5kIG9ubHkgbG9vayBhdCBJIGxvZzEwIGFuZCBJIGxvZyArIFIgbG9nMTAgZm9yIGNvbXBhcmlzb24KbWNfYnVyc3RfZHluLmRmX3AgPC0gbWNfYnVyc3RfZHluLmRmICU+JSAKICBmaWx0ZXIoaWQgJWluJSBjKCJJX2xvZyIsIlJfbG9nK0lfbG9nIikgJiB2YXJpYWJsZSA9PSAiY3IiICYgYmV0YV9zaW11bGF0ZWQgIT0gNS43MjEpICU+JSAjIyBvbmx5IGdldCB0aGUgY3VlcyB3ZSB3YW50IHRvIHBsb3QKICBsZWZ0X2pvaW4oZXpfbGFiZWwsIGJ5ID0gImlkIikgJT4lIAogIG11dGF0ZShsb25nX3Nob3J0X2xhYmVsID0gaWZlbHNlKGlkID09ICJSX2xvZytJX2xvZyIsICJSQkMgbG9nICYgYXNleHVhbCBpUkJDIGxvZ1xuKFIgbG9nICYgSSBsb2cpIiwKICAgIHBhc3RlMChsb25nX2xhYmVsLCAiICgiLCBzaG9ydF9sYWJlbCwgIikiKSkpCmBgYAoKCiMjIFBsb3QKYGBge3J9Cm1jX2J1cnN0X2NyLnBsIDwtIGdncGxvdCgpICsKICBnZW9tX2xpbmUoZGF0YSA9IG1jX2J1cnN0X2R5bi5kZl9wICU+JSBmaWx0ZXIoYmV0YV9vcHRpbWl6ZWQgIT0gYmV0YV9zaW11bGF0ZWQpLCAKICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gdmFsdWUsIGdyb3VwID0gYmV0YV9vcHRpbWl6ZWQsIGNvbG9yID0gYmV0YV9vcHRpbWl6ZWQpKSArICMjIG9ubHkgcGxvdHRpbmcgdW5vcHRpbWl6ZWQKICBnZW9tX2xpbmUoZGF0YSA9IG1jX2J1cnN0X2R5bi5kZl9wICU+JSBmaWx0ZXIoYmV0YV9vcHRpbWl6ZWQgPT0gYmV0YV9zaW11bGF0ZWQpLCAKICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gdmFsdWUsIGdyb3VwID0gYmV0YV9vcHRpbWl6ZWQsIGNvbG9yID0gYmV0YV9vcHRpbWl6ZWQpLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIsIHNpemUgPSAxKSArICMjIG9wdGltaXplZCBjciBpcyBpbmRpY2F0ZWQgYnkgYSBkYXNoZWQgbGluZQogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoYmV0YV9zaW11bGF0ZWQpLCBjb2xzID0gdmFycyhsb25nX3Nob3J0X2xhYmVsKSkgKwogIGxhYnMoeCA9ICJUaW1lIChkYXlzKSIsIHkgPSAiQ29udmVyc2lvbiByYXRlIiwgY29sb3IgPSAiT3B0aW1pemVkXG5idXJzdCBzaXplIikgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICB0aGVtZV9idygpICsKICB0aGVtZSgKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIy0tLS0tLSBhcnJhbmdlIHRvZ2V0aGVyIS0tLS0tLS0tLS0tLS0jCmBgYHtyfQojIyBmaXJzdCBhcnJhbmdlIHRoZSB0d28gZml0bmVzcyBncmFwaHMKbWNfYnVyc3RfZml0bmVzc19jb20ucGwgPC0gZ2dhcnJhbmdlKG1jX2J1cnN0X2ZpdG5lc3MucGwsIG1jX2J1cnN0X2RpZmYucGwsIG5jb2wgPSAxLCBhbGlnbiA9ICJodiIsIGxhYmVscyA9IGMoIkEiLCAiQiIpLCBoZWlnaHRzID0gYygwLjYsMC40KSkKCiMjIHB1dCB0aGVtIHRvZ2V0aGVyCmdnYXJyYW5nZShtY19idXJzdF9maXRuZXNzX2NvbS5wbCwgbWNfYnVyc3RfY3IucGwsIG5jb2wgPSAyLCBhbGlnbiA9ICJodiIsIGxhYmVscyA9IGMoIiIsICJDIiksIHdpZHRocyA9IGMoMC41LDAuNTUpKQpnZ3NhdmUodW5pdHMgPSAicHgiLCBkcGkgPSAzMDAsIHdpZHRoID0gMjY1MCwgaGVpZ2h0ID0gMTUwMCwgZmlsZW5hbWUgPSBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZmlndXJlcy9tY19idXJzdF9tYWluLnRpZmYiKSwgYmcgPSAid2hpdGUiLCBzY2FsZSA9IDEuMjYpCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIyBTdXBwbGVtZW50YXJ5IGZpZzogYWxsIHJuIGF0IGRpZmZlcmVudCBidXJzdCBzaXplcwojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIyBQcm9jZXNzIGRhdGEgZm9yIHBsb3R0aW5nCmBgYHtyfQojIyBnZXQgcGxvdHRpbmcgbGFiZWwKbWNfYnVyc3Rfcm4uZGZfcCA8LSBtY19idXJzdF9ybi5kZiAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChlel9sYWJlbCwgaWQsIHNob3J0X2xhYmVsKSwgYnkgPSAiaWQiKSAlPiUgCiAgbXV0YXRlKGxvbmdfc2hvcnRfbGFiZWwgPSBwYXN0ZTAobG9uZ19sYWJlbCwgIiAoIiwgc2hvcnRfbGFiZWwsICIpIikpCgojIyBnZXQgcGxvdHRpbmcgb3JkZXIuIFdlIGFyZSByYW5raW5nIHRoZW0gYmFzZWQgb24gdGhlIGdlb21ldHJpYyBtZWFuIG9mIHVub3B0aW1pemVkIGZpdG5lc3MgKGZvciBhbGwgY29tYmluYXRpb25zKQptY19idXJzdF9vcmRlciA8LSBtY19idXJzdF9maW5hbF9kaWZmLmRmICU+JSAKICBtdXRhdGUobG9uZ19sYWJlbCA9IGlmZWxzZShpZCA9PSAiUl9sb2crSV9sb2ciLCAiUkJDIGxvZyAmIGFzZXh1YWwgaVJCQyBsb2ciLCBsb25nX2xhYmVsKSkgJT4lIAogIG11dGF0ZShsb25nX3Nob3J0X2xhYmVsID0gcGFzdGUwKGxvbmdfbGFiZWwsICIgKCIsIHNob3J0X2xhYmVsLCAiKSIpKSAlPiUgCiAgZ3JvdXBfYnkobG9uZ19zaG9ydF9sYWJlbCkgJT4lIAogIHN1bW1hcmlzZShnZW9tX2ZpdG5lc3MgPSBleHAobWVhbihsb2coZml0bmVzcykpKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKC1nZW9tX2ZpdG5lc3MpCgptY19idXJzdF9ybi5kZl9wJGxvbmdfc2hvcnRfbGFiZWwgPC0gZmFjdG9yKG1jX2J1cnN0X3JuLmRmX3AkbG9uZ19zaG9ydF9sYWJlbCwgbWNfYnVyc3Rfb3JkZXIkbG9uZ19zaG9ydF9sYWJlbCkKCiMjIE5vcm1hbGl6ZSBjdWUgcmFuZ2UgZm9yIHBsb3R0aW5nCm1jX2J1cnN0X3JuLmRmX3BfbiA8LSBtY19idXJzdF9ybi5kZl9wICU+JSAKICBncm91cF9ieShpZCkgJT4lIAogIG11dGF0ZShyYW5nZSA9IG1heChjdWVfcmFuZ2UpIC0gbWluKGN1ZV9yYW5nZSksCiAgICAgICAgIG1lYW4gPSBtZWFuKGN1ZV9yYW5nZSksCiAgICAgICAgIGN1ZV9yYW5nZV9ub3JtID0gKGN1ZV9yYW5nZSAtIG1lYW4pL3JhbmdlKQoKIyMgbWFrZSBydWcgcGxvdCBvZiBSSSB3aWRlciBmb3IgdHJhamVjdG9yeSBwbG90dGluZwptY19idXJzdF9SSV9ydWcud2lkZSA8LSBtY19idXJzdF9SSV9ydWcuZGYgJT4lIAogIHNlbGVjdCh0aW1lLCB2YXJpYWJsZSx2YWx1ZSwgYmV0YSkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB2YXJpYWJsZSwgdmFsdWVzX2Zyb20gPSB2YWx1ZSwgaWRfY29scyA9IGModGltZSwgYmV0YSkpCgptY19idXJzdF9SSV9ydWcud2lkZQpgYGAKCiMjIHBsb3QKYGBge3J9CiMjIHNpbmdsZSBjdWUKbWNfYnVyc3Rfcm5fc2MucGwgPC0gZ2dwbG90KGRhdGEgPSBtY19idXJzdF9ybi5kZl9wX24pICsKICBnZW9tX2xpbmUoYWVzKHggPSBjdWVfcmFuZ2Vfbm9ybSwgeSA9IGNyLCBjb2xvciA9IGJldGEsIGdyb3VwID0gYmV0YSkpICsKICBmYWNldF93cmFwKH5sb25nX3Nob3J0X2xhYmVsLCBzY2FsZXMgPSAiZnJlZSIsIG5jb2wgPSAzKSArCiAgbGFicyh4ID0gIkN1ZSByYW5nZSAobm9ybWFsaXplZCkiLCB5ID0gIkNvbnZlcnNpb24gcmF0ZSIsIGNvbG9yID0gIkJ1cnN0IHNpemUiKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9udW1iZXIoYWNjdXJhY3kgPSAwLjEpKQoKIyMgZHVhbCBjdWUKbWNfYnVyc3Rfcm5fZGMucGwgPC0gZ2dwbG90KCkgKwogIGdlb21fcmFzdGVyKGRhdGEgPSBtY19idXJzdF9ybl9SSS5kZiwgYWVzKHkgPSBjdWVfcmFuZ2UsIHggPSBjdWVfcmFuZ2VfYiwgZmlsbCA9IGNyKSkgKwogIGdlb21fcGF0aChkYXRhID0gbWNfYnVyc3RfUklfcnVnLndpZGUsIGFlcyh4ID0gSSwgeSA9IFIpLCBzaXplID0gMC43NSwgY29sb3IgPSAiY3lhbiIsIGFycm93ID0gYXJyb3coYW5nbGUgPSAzMCwgbGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKSkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBtY19idXJzdF9SSV9ydWcud2lkZSAlPiUgZmlsdGVyKHRpbWUgJSUgMSA9PSAwKSwgYWVzKHggPSBJLCB5ID0gUiksIHNpemUgPSAxLCBjb2xvciA9ICJjeWFuIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJDIikgKwogIGZhY2V0X3dyYXAofmJldGEsIG5jb2wgPSAxKSArCiAgbGFicyh4ID0gIkFzZXh1YWwgaVJCQyBsb2cxMCIsIHkgPSAiUkJDIGxvZzEwIiwgZmlsbCA9ICJDb252ZXJzaW9uXG5yYXRlIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBsb3Quc3VidGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTMpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGxhYmVsX251bWJlcihhY2N1cmFjeSA9IDAuMSksCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoNi44LDcpKQpgYGAKCgpgYGB7cn0KIyMgcGxvdCB0b2dldGhlcgpnZ2FycmFuZ2UobWNfYnVyc3Rfcm5fc2MucGwsIG1jX2J1cnN0X3JuX2RjLnBsLCB3aWR0aHMgPSBjKDIuNSwxLjEpLCBsYWJlbHMgPSBjKCJBIiwgIkIiKSwgbmNvbCA9IDIsIG5yb3cgPSAxLCBhbGlnbiA9ICJodiIpCmdnc2F2ZSh1bml0cyA9ICJweCIsIGRwaSA9IDMwMCwgd2lkdGggPSAyNTUwLCBoZWlnaHQgPSAxODAwLCBmaWxlbmFtZSA9IGhlcmUoImNvZGVfcmVwb3NpdG9yeS9maWd1cmVzL21jX2J1cnN0X3JuLnRpZmYiKSwgYmcgPSAid2hpdGUiLCBzY2FsZSA9IDEpCmBgYAoKIyMgc2FuaXR5IGNoZWNrLiBSSSBybiBpcyBkaWZmZXJlbnQgYmV0d2VlbiB0aGUgYmV0YSAhPSAzLjk4CmBgYHtyfQptY19idXJzdF9ybl9SSS5kZiAlPiUgCiAgZmlsdGVyKGJldGEgIT0gMy45OCkgJT4lIAogIGdyb3VwX2J5KGN1ZV9yYW5nZSwgY3VlX3JhbmdlX2IpICU+JSAKICBtdXRhdGUoc2QgPSBzZChjcikpICU+JSAKICBnZ3Bsb3QoKSArIGdlb21fcmFzdGVyKGFlcyh4ID0gY3VlX3JhbmdlX2IsIHkgPSBjdWVfcmFuZ2UsIGZpbGwgPSBzZCkpCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgSW1wYWN0IG9mIGN1ZSBwZXJjZXB0aW9uIG9uIHZpcnVsZW5jZQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojLS0tLS0tLS0tIERpdmlkIGR5bmFtaWNzIGRhdGEgaW50byBoaWdoIGFuZCBsb3cgcGVyZm9ybWluZyAtLS0tLS0tLS0tLS0tLS0jCiMgU2luZ2xlIGN1ZSAKYGBge3J9CiMgY2xhc3NpZnkgdG9wIDQgcGVyZm9ybWluZyBzaW5nbGUgY3VlcyBhcyAiaGlnaCBwZXJmb3JtaW5nIgp0b3BfNF9zaW5nbGVfY3VlIDwtIHNpX29wdC5kZiAlPiUgCiAgZmlsdGVyKGN1ZSAhPSAidCIpICU+JSAKICBzbGljZV9tYXgobiA9IDQsIG9yZGVyX2J5ID0gZml0bmVzc18yMCkKCiMgZ2V0IHdpZGUgZm9ybWF0IGRhdGFmcmFtZS4gSGVyZSwgd2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIFJCQyBhbmQgdG90YWwgaVJCQyBjb3VudApzaV9kYy5kZiA8LSBzaV9keW5fMzAuZGYgJT4lIAogIG11dGF0ZSh2YWx1ZSA9IGFzLm51bWVyaWModmFsdWUpKSAlPiUgCiAgZmlsdGVyKHZhcmlhYmxlID09ICJJIiB8IHZhcmlhYmxlID09ICJJZyIgfCB2YXJpYWJsZSA9PSAiUiIpICU+JSAKICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHZhcmlhYmxlLCB2YWx1ZXNfZnJvbSA9IHZhbHVlKSAlPiUgCiAgbXV0YXRlKHRvdGFsID0gSStJZykKCiMgc3BsaXQgaW50byB0b3AgYW5kIHBvb3ItcGVyZm9ybWluZyBjdWVzCnNpX2RjLmhpZ2ggPC0gc2lfZGMuZGYgJT4lIGZpbHRlcihpZCAlaW4lIHRvcF80X3NpbmdsZV9jdWUkaWQpCnNpX2RjLnBvb3IgPC0gc2lfZGMuZGYgJT4lIGZpbHRlcighaWQgJWluJSB0b3BfNF9zaW5nbGVfY3VlJGlkKQoKIyBqb2luIGhpZ2ggcGVyZm9ybWluZyB3aXRoIGxhYmVsCnNpX2RjLmhpZ2ggPC0gc2lfZGMuaGlnaCAlPiUgbGVmdF9qb2luKGV6X2xhYmVsICU+JSBkaXN0aW5jdChpZCwgLmtlZXBfYWxsID0gVCksIGJ5ID0gImlkIikKCiMgcmVvcmRlciBjdWVzIGJ5IGRlc2NlbmRpbmcgZml0bmVzcwpzaV9kYy5oaWdoJHNob3J0X2xhYmVsIDwtIGZhY3RvcihzaV9kYy5oaWdoJHNob3J0X2xhYmVsLCB0b3BfNF9zaW5nbGVfY3VlJHNob3J0X2xhYmVsKQpgYGAKCiMgRHVhbCBjdWUKYGBge3J9CiMgY2xhc3NpZnkgdG9wIDQgcGVyZm9ybWluZyBkdWFsIGN1ZXMgYXMgImhpZ2ggcGVyZm9ybWluZyIKdG9wXzRfZHVhbF9jdWUgPC0gZHVhbF9jdWVfZl9maW5hbC5kZiAlPiUgCiAgbXV0YXRlKGxhYmVsX2FsdCA9IHBhc3RlKGxhYmVsLCAiJiIgLCBsYWJlbF9iKSkgJT4lIAogIHNsaWNlX21heChuID0gNCwgb3JkZXJfYnkgPSBmaXRuZXNzKQoKIyBnZXQgd2lkZSBmb3JtYXQgZGF0YWZyYW1lLiBIZXJlLCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgUkJDIGFuZCB0b3RhbCBpUkJDIGNvdW50CmR1YWxfZGMuZGYgPC0gZHVhbF9jdWVfZHluXzMwLmRmICU+JSAKICBtdXRhdGUobGFiZWxfYWx0ID0gcGFzdGUobGFiZWwsICImIiAsIGxhYmVsX2IpKSAlPiUgIyMgZ2V0IG5ldyBsYWJlbAogIGZpbHRlcih2YXJpYWJsZSA9PSAiSSIgfCB2YXJpYWJsZSA9PSAiSWciIHwgdmFyaWFibGUgPT0gIlIiKSAlPiUgIyMgZ2V0IGRlc2lyZWQgdmFyaWFibGVzCiAgdGlkeXI6OnBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB2YXJpYWJsZSwgdmFsdWVzX2Zyb20gPSB2YWx1ZSwgaWRfY29scyA9IGModGltZSwgbGFiZWxfYWx0KSkgJT4lCiAgbXV0YXRlKHRvdGFsID0gSStJZykKCiMgc3BsaXQgaW50byB0b3AgYW5kIHBvb3ItcGVyZm9ybWluZyBjdWVzCmR1YWxfZGMuaGlnaCA8LSBkdWFsX2RjLmRmICU+JSBmaWx0ZXIobGFiZWxfYWx0ICVpbiUgdG9wXzRfZHVhbF9jdWUkbGFiZWxfYWx0KQpkdWFsX2RjLnBvb3IgPC0gZHVhbF9kYy5kZiAlPiUgZmlsdGVyKCFsYWJlbF9hbHQgJWluJSB0b3BfNF9kdWFsX2N1ZSRsYWJlbF9hbHQpCgojIHJlb3JkZXIgY3VlcyBieSBkZXNjZW5kaW5nIGZpdG5lc3MKZHVhbF9kYy5oaWdoJGxhYmVsX2FsdCA8LSBmYWN0b3IoZHVhbF9kYy5oaWdoJGxhYmVsX2FsdCwgdG9wXzRfZHVhbF9jdWUkbGFiZWxfYWx0KQpgYGAKCiMtLS0tLS0tLS0tLS0tUGxvdCBkaXNlYXNlIGN1cnZlcy0tLS0tLS0tLS0tLS0tLS0tIwojIFNpbmdsZSBjdWUKYGBge3J9CiMgcGxvdCBwb29yIHBlcmZvcm1pbmcgY3VlcyAodGhlc2UgYXJlIGdyZXkgbGluZXMpCnNpX2RjX3ByZS5wbHQgPC0gZ2dwbG90KCkgKwogIGdlb21fcGF0aChkYXRhID0gc2lfZGMucG9vciwgYWVzKHg9IHRvdGFsLCB5ID0gUiwgZ3JvdXAgPSBpZCksIGNvbG9yID0gImRhcmsgZ3JleSIsIGFycm93ID0gYXJyb3codHlwZSA9ICJjbG9zZWQiLCBhbmdsZSA9IDEwLCBsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSkgKwogIGxhYnMoY29sb3IgPSAiR29vZCBwZXJmb3JtaW5nIGN1ZXMiLCB4ID0gIlRvdGFsIGlSQkMiLCB5ID0gIlJCQyIpICsKICB0aGVtZV9jbGFzc2ljKCkrIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBmdW5jdGlvbih4KSBmb3JtYXQoeCwgc2NpZW50aWZpYyA9IFRSVUUsIGFjY3VyYWN5ID0gMC4xKSkgKwogIGd1aWRlcyhzaGFwZSA9IEZBTFNFKQoKIyBvdmVybGF5IGR5bmFtaWNzIG9mIGdvb2QgcGVyZm9ybWluZyBjdWVzCnNpX2RjLnBsdCA8LSBzaV9kY19wcmUucGx0ICsKICBnZW9tX3BvaW50KGRhdGEgPSBzaV9kYy5oaWdoICU+JSBmaWx0ZXIocm93X251bWJlcigpICUlIDEwMDAgPT0wKSwgYWVzKHggPSB0b3RhbCwgeSA9IFIsIGNvbG9yID0gc2hvcnRfbGFiZWwsIHNoYXBlID0gc2hvcnRfbGFiZWwpLCBzaXplID0gMykgKwogIGdlb21fcGF0aChkYXRhID0gc2lfZGMuaGlnaCwgYWVzKHg9IHRvdGFsLCB5ID0gUiwgZ3JvdXAgPSBzaG9ydF9sYWJlbCwgY29sb3IgPSBzaG9ydF9sYWJlbCksIHNpemUgPSAxLCBhcnJvdyA9IGFycm93KHR5cGUgPSAiY2xvc2VkIiwgYW5nbGUgPSAxMCwgbGVuZ3RoID0gdW5pdCgwLjIsICJpbmNoZXMiKSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoICIjNDU3NWI0IiwgIiNmZGNiNDQiLCAiIzkxYmZkYiIsICIjZmM4ZDU5IikpICArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsKICBsYWJzKGNvbG9yID0gIkN1ZXMiKSAKYGBgCgojIGR1YWwgY3VlCmBgYHtyfQojIHBsb3QgcG9vciBwZXJmb3JtaW5nIGN1ZXMgKHRoZXNlIGFyZSBncmV5IGxpbmVzKQpkdWFsX2RjX3ByZS5wbHQgPC0gZ2dwbG90KCkgKwogIGdlb21fcGF0aChkYXRhID0gZHVhbF9kYy5wb29yLCBhZXMoeD0gdG90YWwsIHkgPSBSLCBncm91cCA9IGxhYmVsX2FsdCksIGNvbG9yID0gImRhcmsgZ3JleSIsIGFycm93ID0gYXJyb3codHlwZSA9ICJjbG9zZWQiLCBhbmdsZSA9IDEwLCBsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSkgKwogIGxhYnMoY29sb3IgPSAiR29vZCBwZXJmb3JtaW5nIGN1ZXMiLCB4ID0gIlRvdGFsIGlSQkMiLCB5ID0gIlJCQyIpICsKICB0aGVtZV9jbGFzc2ljKCkrIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBmdW5jdGlvbih4KSBmb3JtYXQoeCwgc2NpZW50aWZpYyA9IFRSVUUsIGFjY3VyYWN5ID0gMC4xKSkgKwogIGd1aWRlcyhzaGFwZSA9IEZBTFNFKQoKIyBvdmVybGF5IGR5bmFtaWNzIG9mIGdvb2QgcGVyZm9ybWluZyBjdWVzCmR1YWxfZGMucGx0IDwtIGR1YWxfZGNfcHJlLnBsdCArCiAgZ2VvbV9wb2ludChkYXRhID0gZHVhbF9kYy5oaWdoICU+JSBmaWx0ZXIocm93X251bWJlcigpICUlIDEwMCA9PTApLCBhZXMoeCA9IHRvdGFsLCB5ID0gUiwgY29sb3IgPSBsYWJlbF9hbHQsIHNoYXBlID0gbGFiZWxfYWx0KSwgc2l6ZSA9IDIpICsKICBnZW9tX3BhdGgoZGF0YSA9IGR1YWxfZGMuaGlnaCwgYWVzKHg9IHRvdGFsLCB5ID0gUiwgZ3JvdXAgPSBsYWJlbF9hbHQsIGNvbG9yID0gbGFiZWxfYWx0KSwgc2l6ZSA9IDEsIGFycm93ID0gYXJyb3codHlwZSA9ICJjbG9zZWQiLCBhbmdsZSA9IDEwLCBsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YyggIiM0NTc1YjQiLCAiI2ZkY2I0NCIsICIjOTFiZmRiIiwgIiNmYzhkNTkiKSkgICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKwogIGxhYnMoY29sb3IgPSAiQ3VlcyIpIApgYGAKCiMtLS0tLS0tLS1hcmVhIG9mIGN1cnZlIHZzIGZpdG5lc3MtLS0tLS0tLS0tLS0tLS0jCiMgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIGFyZWEgd2l0aGluIGN1cnZlCmBgYHtyfQpnZXRfZGNfYXJlYSA8LSBmdW5jdGlvbihkZil7CiAgeCA8LSBkZiR0b3RhbAogIHkgPC0gZGYkUgogIGNodWxsKHgseSktPmkKICByZXR1cm4oYXJlYXBsKGNiaW5kKHhbaV0seVtpXSkpKQp9CmBgYAoKIyBzaW5nbGUgY3VlIGFyZWEKYGBge3J9CiMgc3BsaXQgZGYKc2lfZGMubHMgPC0gc3BsaXQoc2lfZGMuZGYsIHNpX2RjLmRmJGlkKQoKIyBnZXQgYXJlYQpzaV9kYy5hcmVhIDwtIGNiaW5kLmRhdGEuZnJhbWUoYXJlYSA9IGFzLm51bWVyaWMobGFwcGx5KHNpX2RjLmxzLCBnZXRfZGNfYXJlYSkpLCBpZCA9IG5hbWVzKGxhcHBseShzaV9kYy5scywgZ2V0X2RjX2FyZWEpKSkKCiMgam9pbiB3aXRoIGZpdG5lc3MKc2lfb3B0X2FyZWEuZGYgPC0gc2lfb3B0LmRmICU+JSAKICBmaWx0ZXIoY3VlICE9ICJ0IikgJT4lIAogIGxlZnRfam9pbihzaV9kYy5hcmVhLCBieSA9ICJpZCIpICU+JSAKICBtdXRhdGUoZml0bmVzc19ub3JtID0gZml0bmVzc18yMC85Ljg4MzYwMikgIyMgbm9ybWFsaXplIGZpdG5lc3MgKHVzZSBmaXRuZXNzIG9idGFpbmVkIHVzaW5nIHRpbWUgZGY9OSBnaXZlbiB0aGF0IHdlIGFyZSBwbG90dGluZyBkdWFsIGN1ZSBhbmQgc2luZ2xlIG9uIHRoZSBzYW1lIGdyYXBoISkKCiMgc2FuaXR5IGNoZWNrIGFyZWEuIFRoZSBhcmVhIHNob3VsZCBiZSBvbiB0aGUgb3JkZXIgaWYgd2UgYXNzdW1lIGEgcmVjdGFuZ2xlIQpzaV9kYy5kZiAlPiUgCiAgZ3JvdXBfYnkoaWQpICU+JSAKICBzdW1tYXJpc2UoUl9kaWZmID0gbWF4KFIpLW1pbihSKSwKICAgICAgICAgICAgdG90YWxfZGlmZiA9IG1heCh0b3RhbCktbWluKHRvdGFsKSwKICAgICAgICAgICAgcHJvZHVjdCA9IFJfZGlmZip0b3RhbF9kaWZmKQpgYGAKCiMgZHVhbCBjdWUKYGBge3J9CiMgc3BsaXQKZHVhbF9kYy5scyA8LSBzcGxpdChkdWFsX2RjLmRmLCBkdWFsX2RjLmRmJGxhYmVsX2FsdCkKCiMgZ2V0IGFyZWEKZHVhbF9kYy5hcmVhIDwtIGNiaW5kLmRhdGEuZnJhbWUoYXJlYSA9IGFzLm51bWVyaWMobGFwcGx5KGR1YWxfZGMubHMsIGdldF9kY19hcmVhKSksIGxhYmVsX2FsdCA9IG5hbWVzKGxhcHBseShkdWFsX2RjLmxzLCBnZXRfZGNfYXJlYSkpKQoKIyBqb2luIHdpdGggZml0bmVzcwpkdWFsX2N1ZV9mX2ZpbmFsX2FyZWEuZGYgPC0gZHVhbF9jdWVfZl9maW5hbC5kZiAlPiUgCiAgbXV0YXRlKGxhYmVsX2FsdCA9IHBhc3RlKGxhYmVsLCAiJiIgLCBsYWJlbF9iKSkgJT4lIAogIGxlZnRfam9pbihkdWFsX2RjLmFyZWEsIGJ5ID0gYygibGFiZWxfYWx0IikpICU+JSAKICBtdXRhdGUoZml0bmVzc19ub3JtID0gZml0bmVzcy85Ljg4MzYwMikgIyMgbm9ybWFsaXplIGZpdG5lc3MgKHVzZSBmaXRuZXNzIG9idGFpbmVkIHVzaW5nIHRpbWUgZGY9OSBnaXZlbiB0aGF0IHdlIGFyZSBwbG90dGluZyBkdWFsIGN1ZSBhbmQgc2luZ2xlIG9uIHRoZSBzYW1lIGdyYXBoISkKYGBgCgojIGZpdCBHTE0KYGBge3J9CiMgZml0IGxtCnNpX2xtIDwtIGxtKGZpdG5lc3Nfbm9ybSB+IGFyZWEsIGRhdGEgPSBzaV9vcHRfYXJlYS5kZikKZHVhbF9sbSA8LSBsbShmaXRuZXNzX25vcm0gfiBhcmVhLCBkYXRhID0gZHVhbF9jdWVfZl9maW5hbF9hcmVhLmRmKQoKIyBnZXQgcHJlZGljdGlvbiB3aXRoIDk1JSBDSQpzaV9sbS5jaSA9IHByZWRpY3Qoc2lfbG0sIGludGVydmFsID0gImNvbmZpZGVuY2UiKSAlPiUgY2JpbmQuZGF0YS5mcmFtZShhcmVhID0gc2lfb3B0X2FyZWEuZGYkYXJlYSkKZHVhbF9sbS5jaSA9IHByZWRpY3QoZHVhbF9sbSwgaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpICU+JSBjYmluZC5kYXRhLmZyYW1lKGFyZWEgPSBkdWFsX2N1ZV9mX2ZpbmFsX2FyZWEuZGYkYXJlYSkKCiMgbG9vayBhdCBSXjIKc3VtbWFyeShzaV9sbSkkci5zcXVhcmVkICMjIDAuMTc2NTI0OApzdW1tYXJ5KGR1YWxfbG0pJHIuc3F1YXJlZCAjIyAwLjEwNTM1OTEKYGBgCgojIHBsb3QgY29ycmVsYXRpb24KYGBge3J9CiMgc2luZ2xlIGN1ZQpzaV9jb3JyX2FyZWEucGx0IDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBzaV9vcHRfYXJlYS5kZiwgYWVzKHggPSBhcmVhLCB5ID0gZml0bmVzc19ub3JtLCBjb2xvciA9ICJTaW5nbGUgY3VlIiwgc2hhcGUgPSAiU2luZ2xlIGN1ZSIpLCBzaXplID0gMywgYWxwaGEgPSAwLjcpICsKICBnZW9tX2xpbmUoZGF0YSA9IHNpX2xtLmNpLCBhZXMoeCA9IGFyZWEsIGZpdCkpICsKICBnZW9tX3JpYmJvbihkYXRhID0gc2lfbG0uY2ksIGFlcyh4ID0gYXJlYSwgeW1pbiA9IGx3ciwgeW1heCA9IHVwciksIGFscGhhID0gLjE1KSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgbGFiZWwgPSAiUl4yPTAuMTgiLCB4ID0gMS42KigxMF4xMSksIHkgPSAwLjc2KSArCiAgbGFicyh4ID0gIkFyZWEiLCB5ID0gIk5vcm1hbGl6ZWQgZml0bmVzcyIsIHNoYXBlID0gIkN1ZSBjYXRlZ29yeSIsIGNvbG9yID0gIkN1ZSBjYXRlZ29yeSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhvcmFuZ2UpKSArCiAgeGxpbShtaW4oc2lfb3B0X2FyZWEuZGYkYXJlYSksIG1heChkdWFsX2N1ZV9mX2ZpbmFsX2FyZWEuZGYkYXJlYSkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgZHVhbCBjdWUKZHVhbF9jb3JyX2FyZWEucGx0IDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkdWFsX2N1ZV9mX2ZpbmFsX2FyZWEuZGYsIGFlcyh4ID0gYXJlYSwgeSA9IGZpdG5lc3Nfbm9ybSwgY29sb3IgPSAiRHVhbCBjdWUiLCBzaGFwZSA9ICJEdWFsIGN1ZSIpLCBzaXplID0gMywgYWxwaGEgPSAwLjcpICsKICBnZW9tX2xpbmUoZGF0YSA9IGR1YWxfbG0uY2ksIGFlcyh4ID0gYXJlYSwgZml0KSkgKwogIGdlb21fcmliYm9uKGRhdGEgPSBkdWFsX2xtLmNpLCBhZXMoeCA9IGFyZWEsIHltaW4gPSBsd3IsIHltYXggPSB1cHIpLCBhbHBoYSA9IC4xNSkgKwogYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgbGFiZWwgPSAiUl4yPTAuMTEiLCB4ID0gMS42KigxMF4xMSksIHkgPSAwLjc2KSArCiAgbGFicyh4ID0gIkFyZWEiLCB5ID0gIk5vcm1hbGl6ZWQgZml0bmVzcyIsIHNoYXBlID0gIkN1ZSBjYXRlZ29yeSIsIGNvbG9yID0gIkN1ZSBjYXRlZ29yeSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhibHVlKSkgKwogIHhsaW0obWluKHNpX29wdF9hcmVhLmRmJGFyZWEpLCBtYXgoZHVhbF9jdWVfZl9maW5hbF9hcmVhLmRmJGFyZWEpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKIy0tLS0tLS0tLWFzc2VtYmxlIGZpZ3VyZS0tLS0tLS0tLS0tLSMKYGBge3J9CnNpX2RjX2FyZWEucGx0IDwtIGdnYXJyYW5nZShzaV9kYy5wbHQsIHNpX2NvcnJfYXJlYS5wbHQsIG5jb2wgPSAyLCBucm93ID0gMSwgYWxpZ24gPSAiaHYiLCBjb21tb24ubGVnZW5kID0gVCwgbGFiZWxzID0gYygiQSIsICJCIikpCmR1YWxfZGNfYXJlYS5wbHQgPC0gZ2dhcnJhbmdlKGR1YWxfZGMucGx0LCBkdWFsX2NvcnJfYXJlYS5wbHQsIG5jb2wgPSAyLCBucm93ID0gMSwgYWxpZ24gPSAiaHYiLCBjb21tb24ubGVnZW5kID0gVCwgbGFiZWxzID0gYygiQyIsICJEIikpCgpnZ2FycmFuZ2Uoc2lfZGNfYXJlYS5wbHQsIGR1YWxfZGNfYXJlYS5wbHQsIG5yb3cgPSAyLCBhbGlnbiA9ICJ2IikKCmdnc2F2ZSh1bml0cyA9ICJweCIsIGRwaSA9IDMwMCwgd2lkdGggPSAyNTUwLCBoZWlnaHQgPSAyMDAwLCBmaWxlbmFtZSA9IGhlcmUoImNvZGVfcmVwb3NpdG9yeS9maWd1cmVzL3ZpcnVsZW5jZS50aWZmIiksIGJnID0gIndoaXRlIiwgc2NhbGUgPSAxKQpgYGAKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgVmFsaWRhdGlvbiBvZiBvcHRpbWl6YXRpb24KIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojLS0tLS0tIHByb2Nlc3MgaW5wdXQgZGF0YSAtLS0tLS0jCiMgUHJvY2VzcyBkYXRhCmBgYHtyfQojIyBnZXQgZGlmZmVyZW5jZSBiZXR3ZWVudCBmaXRuZXNzIGNvbmZlcnJlZCBieSBtb2RlbCBwcm9kdWNlZCBieSBhIHJhbmRvbSBzcGxpbmUgc3RyYXRlZ3kgIlYxIiB2cyBvcHRpbWl6ZWQgZml0bmVzcwp2YWxpZGF0aW9uLmRmX3AgPC0gdmFsaWRhdGlvbi5kZiAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChzaV9vcHQuZGYsIGlkLCBmaXRuZXNzXzIwLCBzaG9ydF9sYWJlbCwgbG9uZ19sYWJlbCksIGJ5ID0gYygiaWQiKSkgJT4lIAogIG11dGF0ZShkaWZmID0gZml0bmVzc18yMC1WMSwKICAgICAgICAgbG9uZ19zaG9ydF9sYWJlbCA9IHBhc3RlMChsb25nX2xhYmVsLCAiICgiLCBzaG9ydF9sYWJlbCwgIikiKSkKCiMjIG5vIHJhbmRvbSBzdHJhdGVneSBwZXJmb3JtZWQgYmV0dGVyIQp2YWxpZGF0aW9uLmRmX3AgJT4lIGZpbHRlcihkaWZmPD0wKQpgYGAKCiMgUGxvdApgYGB7cn0KZ2dwbG90KHZhbGlkYXRpb24uZGZfcCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IGRpZmYpLCBmaWxsID0gImxpZ2h0IGdyZXkiKSArCiAgZmFjZXRfd3JhcCh+bG9uZ19zaG9ydF9sYWJlbCwgc2NhbGVzID0gImZyZWUiKSArCiAgbGFicyh4ID0gIkZpdG5lc3MgZGlmZmVyZW5jZSAoT3B0aW1hbC1SYW5kb20pIiwgeSA9ICJEZW5zaXR5IikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgCgpnZ3NhdmUodW5pdHMgPSAicHgiLCBkcGkgPSAzMDAsIHdpZHRoID0gMjU1MCwgaGVpZ2h0ID0gMTUwMCwgZmlsZW5hbWUgPSBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZmlndXJlcy9zaV92YWxpZGF0aW9uLnRpZmYiKSwgYmcgPSAid2hpdGUiLCBzY2FsZSA9IDEpCmBgYAoKCgoKCg==